X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=android%2Fproject%2Fxscreensaver%2Fsrc%2Forg%2Fjwz%2Fxscreensaver%2FXScreenSaverDaydream.java;fp=android%2Fproject%2Fxscreensaver%2Fsrc%2Forg%2Fjwz%2Fxscreensaver%2FXScreenSaverDaydream.java;h=ea04df070cc333300ab9ad64abedb21ce8236352;hp=0000000000000000000000000000000000000000;hb=aa75c7476aeaa84cf3abc192b376a8b03c325213;hpb=88cfe534a698a0562e81345957a50714af1453bc diff --git a/android/project/xscreensaver/src/org/jwz/xscreensaver/XScreenSaverDaydream.java b/android/project/xscreensaver/src/org/jwz/xscreensaver/XScreenSaverDaydream.java new file mode 100644 index 00000000..ea04df07 --- /dev/null +++ b/android/project/xscreensaver/src/org/jwz/xscreensaver/XScreenSaverDaydream.java @@ -0,0 +1,289 @@ +/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * xscreensaver, Copyright (c) 2016 Jamie Zawinski + * + * 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 java.lang.Exception; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.KeyEvent; +import android.service.dreams.DreamService; +import android.opengl.GLSurfaceView; +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.app.AlertDialog; +import android.content.DialogInterface; +import android.util.Log; + +public class XScreenSaverDaydream extends DreamService + implements GestureDetector.OnGestureListener, + GestureDetector.OnDoubleTapListener, + Handler.Callback { + + private GLSurfaceView glview; + private int api; + XScreenSaverRenderer renderer; + private GestureDetector detector; + boolean button_down_p; + Bitmap screenshot; + + private void LOG (String fmt, Object... args) { + Log.d ("xscreensaver", + this.getClass().getSimpleName() + ": " + + String.format (fmt, args)); + } + + protected XScreenSaverDaydream (int api) { + super(); + this.api = api; + } + + // Called when jwxyz_abort() is called, or other exceptions are thrown. + // + public boolean handleMessage (Message msg) { + + String err = msg.obj.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(); + } + }); + + return true; + } + + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + + setInteractive (true); + setFullscreen (true); + saveScreenshot(); + + // Extract the saver name from e.g. "BouncingCowDaydream" + String name = this.getClass().getSimpleName(); + String tail = "Daydream"; + if (name.endsWith(tail)) + name = name.substring (0, name.length() - tail.length()); + name = name.toLowerCase(); + + WindowManager wm = (WindowManager) getSystemService (WINDOW_SERVICE); + renderer = + new XScreenSaverRenderer (name, api, getApplicationContext(), wm, + screenshot, this); + + glview = new GLSurfaceView (this); + glview.setRenderer (renderer); + setContentView (glview); + + detector = new GestureDetector (this, this); + } + + public void onDreamingStarted() { + super.onDreamingStarted(); + } + + public void onDreamingStopped() { + super.onDreamingStopped(); + glview.onPause(); + } + + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + glview.onPause(); + } + + + // 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); + } + } + } + + + + /* We distinguish between taps and drags. + + - Drags/pans (down, motion, up) are sent to the saver to handle. + - Single-taps exit the saver. + - Long-press single-taps are sent to the saver as ButtonPress/Release; + - Double-taps are sent to the saver as a "Space" keypress. + + #### TODO: + - Swipes (really, two-finger drags/pans) send Up/Down/Left/RightArrow. + */ + + @Override + public boolean onSingleTapConfirmed (MotionEvent event) { + this.finish(); // Exit the Daydream + return true; + } + + @Override + public boolean onDoubleTap (MotionEvent event) { + renderer.sendKeyEvent (new KeyEvent (KeyEvent.ACTION_DOWN, + KeyEvent.KEYCODE_SPACE)); + return true; + } + + @Override + public void onLongPress (MotionEvent event) { + if (! button_down_p) { + int x = (int) event.getX (event.getPointerId (0)); + int y = (int) event.getY (event.getPointerId (0)); + renderer.sendButtonEvent (x, y, true); + renderer.sendButtonEvent (x, y, false); + } + } + + @Override + public void onShowPress (MotionEvent event) { + if (! button_down_p) { + button_down_p = true; + int x = (int) event.getX (event.getPointerId (0)); + int y = (int) event.getY (event.getPointerId (0)); + renderer.sendButtonEvent (x, y, true); + } + } + + @Override + public boolean onScroll (MotionEvent e1, MotionEvent e2, + float distanceX, float distanceY) { + if (button_down_p) + renderer.sendMotionEvent ((int) e2.getX (e2.getPointerId (0)), + (int) e2.getY (e2.getPointerId (0))); + return true; + } + + // If you drag too fast, you get a single onFling event instead of a + // succession of onScroll events. I can't figure out how to disable it. + @Override + public boolean onFling (MotionEvent e1, MotionEvent e2, + float velocityX, float velocityY) { + return false; + } + + public boolean dragEnded (MotionEvent event) { + if (button_down_p) { + int x = (int) event.getX (event.getPointerId (0)); + int y = (int) event.getY (event.getPointerId (0)); + renderer.sendButtonEvent (x, y, false); + button_down_p = false; + } + return true; + } + + @Override + public boolean onDown (MotionEvent event) { + return false; + } + + @Override + public boolean onSingleTapUp (MotionEvent event) { + return false; + } + + @Override + public boolean onDoubleTapEvent (MotionEvent event) { + return false; + } + + @Override + public boolean dispatchTouchEvent (MotionEvent event) { + detector.onTouchEvent (event); + if (event.getAction() == MotionEvent.ACTION_UP) + dragEnded (event); + return super.dispatchTouchEvent (event); + } + + @Override + public boolean dispatchKeyEvent (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. + + super.dispatchKeyEvent (event); + renderer.sendKeyEvent (event); + return true; + } + + // Dunno what dispatchKeyShortcutEvent does, but apparently nothing useful. + +}