1 /* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * xscreensaver, Copyright (c) 2016 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
12 * The superclass of every saver's Daydream.
14 * Each Daydream needs a distinct subclass in order to show up in the list.
15 * We know which saver we are running by the subclass name; we know which
16 * API to use by how the subclass calls super().
19 package org.jwz.xscreensaver;
21 import java.lang.Exception;
22 import android.view.View;
23 import android.view.Window;
24 import android.view.WindowManager;
25 import android.view.KeyEvent;
26 import android.service.dreams.DreamService;
27 import android.opengl.GLSurfaceView;
28 import android.view.GestureDetector;
29 import android.view.KeyEvent;
30 import android.view.MotionEvent;
31 import android.graphics.Bitmap;
32 import android.graphics.Canvas;
33 import android.os.Message;
34 import android.os.Handler;
35 import android.os.Looper;
36 import android.app.AlertDialog;
37 import android.content.DialogInterface;
38 import android.util.Log;
40 public class XScreenSaverDaydream extends DreamService
41 implements GestureDetector.OnGestureListener,
42 GestureDetector.OnDoubleTapListener,
45 private GLSurfaceView glview;
46 XScreenSaverRenderer renderer;
47 private GestureDetector detector;
48 boolean button_down_p;
51 private void LOG (String fmt, Object... args) {
52 Log.d ("xscreensaver",
53 this.getClass().getSimpleName() + ": " +
54 String.format (fmt, args));
57 protected XScreenSaverDaydream () {
61 // Called when jwxyz_abort() is called, or other exceptions are thrown.
63 public boolean handleMessage (Message msg) {
65 String err = msg.obj.toString();
66 LOG ("Caught exception: %s", err);
68 this.finish(); // Exit the Daydream
70 final AlertDialog.Builder b = new AlertDialog.Builder(this);
72 b.setCancelable (false);
73 b.setPositiveButton ("Bummer",
74 new DialogInterface.OnClickListener() {
75 public void onClick(DialogInterface d, int id) {
79 // #### This isn't working:
80 // "Attempted to add window with non-application token"
81 // "Unable to add window -- token null is not for an application"
82 // I think I need to get an "Activity" to run it on somehow?
84 new Handler (Looper.getMainLooper()).post (new Runnable() {
86 AlertDialog alert = b.create();
87 alert.setTitle (this.getClass().getSimpleName() + " crashed");
88 alert.setIcon(android.R.drawable.ic_dialog_alert);
98 public void onAttachedToWindow() {
99 super.onAttachedToWindow();
101 setInteractive (true);
102 setFullscreen (true);
105 glview = new GLSurfaceView (this);
107 new XScreenSaverRenderer (XScreenSaverRenderer.saverNameOf (this),
108 getApplicationContext(), screenshot,
110 setContentView (glview);
112 detector = new GestureDetector (this, this);
115 public void onDreamingStarted() {
116 super.onDreamingStarted();
119 public void onDreamingStopped() {
120 super.onDreamingStopped();
124 public void onDetachedFromWindow() {
125 super.onDetachedFromWindow();
130 // At startup, before we have blanked the screen, save a screenshot
131 // for later use by the hacks.
133 private void saveScreenshot() {
134 View view = getWindow().getDecorView().getRootView();
136 LOG ("unable to get root view for screenshot");
139 // This doesn't work:
141 boolean was = view.isDrawingCacheEnabled();
142 if (!was) view.setDrawingCacheEnabled (true);
143 view.buildDrawingCache();
144 screenshot = view.getDrawingCache();
145 if (!was) view.setDrawingCacheEnabled (false);
146 if (screenshot == null) {
147 LOG ("unable to get screenshot bitmap from %s", view.toString());
149 screenshot = Bitmap.createBitmap (screenshot);
153 // This doesn't work either: width and height are both -1...
155 int w = view.getLayoutParams().width;
156 int h = view.getLayoutParams().height;
157 if (w <= 0 || h <= 0) {
158 LOG ("unable to get root view for screenshot");
160 screenshot = Bitmap.createBitmap (w, h, Bitmap.Config.ARGB_8888);
161 Canvas c = new Canvas (screenshot);
162 view.layout (0, 0, w, h);
170 /* We distinguish between taps and drags.
172 - Drags/pans (down, motion, up) are sent to the saver to handle.
173 - Single-taps exit the saver.
174 - Long-press single-taps are sent to the saver as ButtonPress/Release;
175 - Double-taps are sent to the saver as a "Space" keypress.
178 - Swipes (really, two-finger drags/pans) send Up/Down/Left/RightArrow.
182 public boolean onSingleTapConfirmed (MotionEvent event) {
183 this.finish(); // Exit the Daydream
188 public boolean onDoubleTap (MotionEvent event) {
189 renderer.sendKeyEvent (new KeyEvent (KeyEvent.ACTION_DOWN,
190 KeyEvent.KEYCODE_SPACE));
195 public void onLongPress (MotionEvent event) {
196 if (! button_down_p) {
197 int x = (int) event.getX (event.getPointerId (0));
198 int y = (int) event.getY (event.getPointerId (0));
199 renderer.sendButtonEvent (x, y, true);
200 renderer.sendButtonEvent (x, y, false);
205 public void onShowPress (MotionEvent event) {
206 if (! button_down_p) {
207 button_down_p = true;
208 int x = (int) event.getX (event.getPointerId (0));
209 int y = (int) event.getY (event.getPointerId (0));
210 renderer.sendButtonEvent (x, y, true);
215 public boolean onScroll (MotionEvent e1, MotionEvent e2,
216 float distanceX, float distanceY) {
218 renderer.sendMotionEvent ((int) e2.getX (e2.getPointerId (0)),
219 (int) e2.getY (e2.getPointerId (0)));
223 // If you drag too fast, you get a single onFling event instead of a
224 // succession of onScroll events. I can't figure out how to disable it.
226 public boolean onFling (MotionEvent e1, MotionEvent e2,
227 float velocityX, float velocityY) {
231 public boolean dragEnded (MotionEvent event) {
233 int x = (int) event.getX (event.getPointerId (0));
234 int y = (int) event.getY (event.getPointerId (0));
235 renderer.sendButtonEvent (x, y, false);
236 button_down_p = false;
242 public boolean onDown (MotionEvent event) {
247 public boolean onSingleTapUp (MotionEvent event) {
252 public boolean onDoubleTapEvent (MotionEvent event) {
257 public boolean dispatchTouchEvent (MotionEvent event) {
258 detector.onTouchEvent (event);
259 if (event.getAction() == MotionEvent.ACTION_UP)
261 return super.dispatchTouchEvent (event);
265 public boolean dispatchKeyEvent (KeyEvent event) {
267 // In the emulator, this doesn't receive keyboard arrow keys, PgUp, etc.
268 // Some other keys like "Home" are interpreted before we get here, and
269 // function keys do weird shit.
271 super.dispatchKeyEvent (event);
272 renderer.sendKeyEvent (event);
276 // Dunno what dispatchKeyShortcutEvent does, but apparently nothing useful.