summaryrefslogblamecommitdiffstats
path: root/android/xscreensaver/src/org/jwz/xscreensaver/Daydream.java
blob: 372af95ce40dbd9d7482488e626e079ec1d28877 (plain) (tree)












































































































































































































































































                                                                              
/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 * xscreensaver, Copyright (c) 2016-2017 Jamie Zawinski <jwz@jwz.org>
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation.  No representations are made about the suitability of this
 * software for any purpose.  It is provided "as is" without express or 
 * implied warranty.
 *
 * The superclass of every saver's Daydream.
 *
 * Each Daydream needs a distinct subclass in order to show up in the list.
 * We know which saver we are running by the subclass name; we know which
 * API to use by how the subclass calls super().
 */

package org.jwz.xscreensaver;

import android.view.Display;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.KeyEvent;
import android.service.dreams.DreamService;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Message;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;

public class Daydream extends DreamService {

  private class SaverView extends SurfaceView
    implements SurfaceHolder.Callback {

    private boolean initTried = false;
    private jwxyz jwxyz_obj;

    private GestureDetector detector;

    private Runnable on_quit = new Runnable() {
      @Override
      public void run() {
        finish();  // Exit the Daydream
      }
    };

    SaverView () {
      super (Daydream.this);
      getHolder().addCallback(this);
    }

    @Override
    public void surfaceChanged (SurfaceHolder holder, int format,
                                int width, int height) {

      if (width == 0 || height == 0) {
        detector = null;
        jwxyz_obj.close();
        jwxyz_obj = null;
      }

      Log.d ("xscreensaver",
             String.format("surfaceChanged: %dx%d", width, height));

      /*
      double r = 0;

      Display d = view.getDisplay();

      if (d != null) {
        switch (d.getRotation()) {
        case Surface.ROTATION_90:  r = 90;  break;
        case Surface.ROTATION_180: r = 180; break;
        case Surface.ROTATION_270: r = 270; break;
        }
      }
      */

      if (jwxyz_obj == null) {
        jwxyz_obj = new jwxyz (jwxyz.saverNameOf (Daydream.this),
                               Daydream.this, screenshot, width, height,
                               holder.getSurface(), on_quit);
        detector = new GestureDetector (Daydream.this, jwxyz_obj);
      } else {
        jwxyz_obj.resize (width, height);
      }

      jwxyz_obj.start();
    }

    @Override
    public void surfaceCreated (SurfaceHolder holder) {
      if (!initTried) {
        initTried = true;
      } else {
        if (jwxyz_obj != null) {
          jwxyz_obj.close();
          jwxyz_obj = null;
        }
      }
    }

    @Override
    public void surfaceDestroyed (SurfaceHolder holder) {
      if (jwxyz_obj != null) {
        jwxyz_obj.close();
        jwxyz_obj = null;
      }
    }

    @Override
    public boolean onTouchEvent (MotionEvent event) {
      detector.onTouchEvent (event);
      if (event.getAction() == MotionEvent.ACTION_UP)
        jwxyz_obj.dragEnded (event);
      return true;
    }

    @Override
    public boolean onKeyDown (int keyCode, KeyEvent event) {
      // In the emulator, this doesn't receive keyboard arrow keys, PgUp, etc.
      // Some other keys like "Home" are interpreted before we get here, and
      // function keys do weird shit.

      // TODO: Does this still work? And is the above still true?

      if (view.jwxyz_obj != null)
        view.jwxyz_obj.sendKeyEvent (event);
      return true;
    }
  }

  private SaverView view;
  Bitmap screenshot;

  private void LOG (String fmt, Object... args) {
    Log.d ("xscreensaver",
           this.getClass().getSimpleName() + ": " +
           String.format (fmt, args));
  }

  protected Daydream () {
    super();
  }

  // Called when jwxyz_abort() is called, or other exceptions are thrown.
  //
/*
  @Override
  public void uncaughtException (Thread thread, Throwable ex) {

    renderer = null;
    String err = ex.toString();
    LOG ("Caught exception: %s", err);

    this.finish();  // Exit the Daydream

    final AlertDialog.Builder b = new AlertDialog.Builder(this);
    b.setMessage (err);
    b.setCancelable (false);
    b.setPositiveButton ("Bummer",
                         new DialogInterface.OnClickListener() {
                           public void onClick(DialogInterface d, int id) {
                           }
                         });

    // #### This isn't working:
    // "Attempted to add window with non-application token"
    // "Unable to add window -- token null is not for an application"
    // I think I need to get an "Activity" to run it on somehow?

    new Handler (Looper.getMainLooper()).post (new Runnable() {
        public void run() {
          AlertDialog alert = b.create();
          alert.setTitle (this.getClass().getSimpleName() + " crashed");
          alert.setIcon(android.R.drawable.ic_dialog_alert);
          alert.show();
        }
      });

    old_handler.uncaughtException (thread, ex);
  }
*/


  @Override
  public void onAttachedToWindow() {
    super.onAttachedToWindow();

    setInteractive (true);
    setFullscreen (true);
    saveScreenshot();

    view = new SaverView ();
    setContentView (view);
  }

  public void onDreamingStarted() {
    super.onDreamingStarted();
    // view.jwxyz_obj.start();
  }

  public void onDreamingStopped() {
    super.onDreamingStopped();
    view.jwxyz_obj.pause();
  }

  public void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    try {
      if (view.jwxyz_obj != null)
        view.jwxyz_obj.pause();
    } catch (Exception exc) {
      // Fun fact: Android swallows exceptions coming from here, then crashes
      // elsewhere.
      LOG ("onDetachedFromWindow: %s", exc.toString());
      throw exc;
    }
  }


  // At startup, before we have blanked the screen, save a screenshot
  // for later use by the hacks.
  //
  private void saveScreenshot() {
    View view = getWindow().getDecorView().getRootView();
    if (view == null) {
      LOG ("unable to get root view for screenshot");
    } else {

      // This doesn't work:
  /*
      boolean was = view.isDrawingCacheEnabled();
      if (!was) view.setDrawingCacheEnabled (true);
      view.buildDrawingCache();
      screenshot = view.getDrawingCache();
      if (!was) view.setDrawingCacheEnabled (false);
      if (screenshot == null) {
        LOG ("unable to get screenshot bitmap from %s", view.toString());
      } else {
        screenshot = Bitmap.createBitmap (screenshot);
      }
   */

      // This doesn't work either: width and height are both -1...

      int w = view.getLayoutParams().width;
      int h = view.getLayoutParams().height;
      if (w <= 0 || h <= 0) {
        LOG ("unable to get root view for screenshot");
      } else {
        screenshot = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas (screenshot);
        view.layout (0, 0, w, h);
        view.draw (c);
      }
    }
  }
}