From d3a98cf6cbc3bd0b9efc570f58e8812c03931c18 Mon Sep 17 00:00:00 2001
From: Simon Rettberg
Date: Tue, 16 Oct 2018 10:08:48 +0200
Subject: Original 5.40
---
android/Makefile | 418 ++++
android/README | 189 ++
android/android.iml | 19 +
android/build.gradle | 30 +
android/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes
android/gradle/wrapper/gradle-wrapper.properties | 6 +
android/gradlew | 160 ++
android/gradlew.bat | 90 +
android/local.properties | 11 +
android/screenhack-android.c | 209 ++
android/settings.gradle | 1 +
.../.idea/caches/build_file_checksums.ser | Bin 0 -> 521 bytes
android/xscreensaver/.idea/codeStyles/Project.xml | 29 +
android/xscreensaver/.idea/compiler.xml | 22 +
android/xscreensaver/.idea/gradle.xml | 19 +
.../Gradle__android_arch_core_common_1_1_0_jar.xml | 11 +
.../Gradle__android_arch_core_runtime_1_1_0.xml | 12 +
...le__android_arch_lifecycle_common_1_1_0_jar.xml | 11 +
..._android_arch_lifecycle_livedata_core_1_1_0.xml | 12 +
...radle__android_arch_lifecycle_runtime_1_1_0.xml | 12 +
...dle__android_arch_lifecycle_viewmodel_1_1_0.xml | 12 +
...roid_support_support_annotations_27_1_1_jar.xml | 11 +
...__com_android_support_support_compat_27_1_1.xml | 12 +
..._com_android_support_support_core_ui_27_1_1.xml | 12 +
...m_android_support_support_core_utils_27_1_1.xml | 12 +
...com_android_support_support_fragment_27_1_1.xml | 12 +
...android_support_support_media_compat_27_1_1.xml | 12 +
...adle__com_android_support_support_v4_27_1_1.xml | 12 +
android/xscreensaver/.idea/misc.xml | 34 +
android/xscreensaver/.idea/modules.xml | 9 +
android/xscreensaver/.idea/runConfigurations.xml | 12 +
android/xscreensaver/.idea/workspace.xml | 2227 ++++++++++++++++++++
android/xscreensaver/assets/fonts/OCRAStd.otf | 1 +
.../xscreensaver/assets/fonts/PxPlus_IBM_VGA8.ttf | 1 +
android/xscreensaver/assets/fonts/YearlReg.ttf | 1 +
android/xscreensaver/build.gradle | 109 +
android/xscreensaver/build.xml | 92 +
.../xscreensaver/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 53636 bytes
.../gradle/wrapper/gradle-wrapper.properties | 6 +
android/xscreensaver/gradlew | 160 ++
android/xscreensaver/gradlew.bat | 90 +
android/xscreensaver/jni/Android.mk | 199 ++
android/xscreensaver/jni/Application.mk | 7 +
android/xscreensaver/local.properties | 11 +
android/xscreensaver/project.properties | 15 +
android/xscreensaver/res/drawable-ldpi/icon.png | Bin 0 -> 2134 bytes
android/xscreensaver/res/drawable-mdpi/icon.png | Bin 0 -> 3039 bytes
android/xscreensaver/res/drawable/thumbnail.png | Bin 0 -> 69607 bytes
.../res/layout-land/activity_xscreensaver.xml | 59 +
.../res/layout/activity_tv_xscreensaver.xml | 41 +
.../res/layout/activity_xscreensaver.xml | 44 +
android/xscreensaver/res/layout/main.xml | 7 +
.../xscreensaver/res/layout/preference_blurb.xml | 45 +
.../xscreensaver/res/layout/slider_preference.xml | 60 +
.../src/org/jwz/xscreensaver/Activity.java | 169 ++
.../xscreensaver/src/org/jwz/xscreensaver/App.java | 22 +
.../src/org/jwz/xscreensaver/Daydream.java | 269 +++
.../src/org/jwz/xscreensaver/Settings.java | 179 ++
.../src/org/jwz/xscreensaver/SliderPreference.java | 160 ++
.../src/org/jwz/xscreensaver/TTFAnalyzer.java | 153 ++
.../src/org/jwz/xscreensaver/TVActivity.java | 50 +
.../src/org/jwz/xscreensaver/Wallpaper.java | 128 ++
.../src/org/jwz/xscreensaver/jwxyz.java | 1115 ++++++++++
android/xscreensaver/xscreensaver.iml | 101 +
64 files changed, 6932 insertions(+)
create mode 100644 android/Makefile
create mode 100644 android/README
create mode 100644 android/android.iml
create mode 100644 android/build.gradle
create mode 100644 android/gradle/wrapper/gradle-wrapper.jar
create mode 100644 android/gradle/wrapper/gradle-wrapper.properties
create mode 100755 android/gradlew
create mode 100644 android/gradlew.bat
create mode 100644 android/local.properties
create mode 100644 android/screenhack-android.c
create mode 100644 android/settings.gradle
create mode 100644 android/xscreensaver/.idea/caches/build_file_checksums.ser
create mode 100644 android/xscreensaver/.idea/codeStyles/Project.xml
create mode 100644 android/xscreensaver/.idea/compiler.xml
create mode 100644 android/xscreensaver/.idea/gradle.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__android_arch_core_common_1_1_0_jar.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__android_arch_core_runtime_1_1_0.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_common_1_1_0_jar.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_livedata_core_1_1_0.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_runtime_1_1_0.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_viewmodel_1_1_0.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_annotations_27_1_1_jar.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_compat_27_1_1.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_core_ui_27_1_1.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_core_utils_27_1_1.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_fragment_27_1_1.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_media_compat_27_1_1.xml
create mode 100644 android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_v4_27_1_1.xml
create mode 100644 android/xscreensaver/.idea/misc.xml
create mode 100644 android/xscreensaver/.idea/modules.xml
create mode 100644 android/xscreensaver/.idea/runConfigurations.xml
create mode 100644 android/xscreensaver/.idea/workspace.xml
create mode 120000 android/xscreensaver/assets/fonts/OCRAStd.otf
create mode 120000 android/xscreensaver/assets/fonts/PxPlus_IBM_VGA8.ttf
create mode 120000 android/xscreensaver/assets/fonts/YearlReg.ttf
create mode 100644 android/xscreensaver/build.gradle
create mode 100644 android/xscreensaver/build.xml
create mode 100644 android/xscreensaver/gradle/wrapper/gradle-wrapper.jar
create mode 100644 android/xscreensaver/gradle/wrapper/gradle-wrapper.properties
create mode 100644 android/xscreensaver/gradlew
create mode 100644 android/xscreensaver/gradlew.bat
create mode 100644 android/xscreensaver/jni/Android.mk
create mode 100644 android/xscreensaver/jni/Application.mk
create mode 100644 android/xscreensaver/local.properties
create mode 100644 android/xscreensaver/project.properties
create mode 100644 android/xscreensaver/res/drawable-ldpi/icon.png
create mode 100644 android/xscreensaver/res/drawable-mdpi/icon.png
create mode 100644 android/xscreensaver/res/drawable/thumbnail.png
create mode 100644 android/xscreensaver/res/layout-land/activity_xscreensaver.xml
create mode 100644 android/xscreensaver/res/layout/activity_tv_xscreensaver.xml
create mode 100644 android/xscreensaver/res/layout/activity_xscreensaver.xml
create mode 100644 android/xscreensaver/res/layout/main.xml
create mode 100644 android/xscreensaver/res/layout/preference_blurb.xml
create mode 100644 android/xscreensaver/res/layout/slider_preference.xml
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/Activity.java
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/App.java
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/Daydream.java
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/Settings.java
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/SliderPreference.java
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/TTFAnalyzer.java
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/TVActivity.java
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/Wallpaper.java
create mode 100644 android/xscreensaver/src/org/jwz/xscreensaver/jwxyz.java
create mode 100644 android/xscreensaver/xscreensaver.iml
(limited to 'android')
diff --git a/android/Makefile b/android/Makefile
new file mode 100644
index 0000000..f0d5da0
--- /dev/null
+++ b/android/Makefile
@@ -0,0 +1,418 @@
+# XScreenSaver for Android
+
+export TERM=dumb
+GRADLE = ./gradlew
+
+default:: debug
+all:: release
+
+clean::
+ $(GRADLE) clean
+
+distdepend::
+
+# Set this to the set of platforms you want to compile for in debug mode.
+# E.g., if you are running an x86 emulator, there's no point in compiling
+# for a dozen other platforms. Release builds override this to "all".
+#
+export APP_ABI = all
+
+
+# TODO:
+# check_versions:
+
+
+# These hacks have interdependencies with others, so we can't build without
+# including them or there are link errors:
+#
+ANDROID_BASE_HACKS= \
+ apple2 \
+ bubble3d \
+ pacman \
+ polyhedra \
+ sonar \
+ sproingies \
+
+# These are the ones that currently work, at least to some degree:
+#
+export ANDROID_HACKS= \
+ $(ANDROID_BASE_HACKS) \
+ abstractile \
+ anemone \
+ anemotaxis \
+ antmaze \
+ antspotlight \
+ apollonian \
+ atlantis \
+ attraction \
+ atunnel \
+ binaryring \
+ blaster \
+ blinkbox \
+ blitspin \
+ blocktube \
+ boing \
+ bouboule \
+ bouncingcow \
+ boxed \
+ boxfit \
+ braid \
+ bsod \
+ bumps \
+ cage \
+ ccurve \
+ celtic \
+ circuit \
+ cityflow \
+ cloudlife \
+ companioncube \
+ compass \
+ coral \
+ crackberg \
+ crumbler \
+ crystal \
+ cube21 \
+ cubenetic \
+ cubestack \
+ cubestorm \
+ cubetwist \
+ cubicgrid \
+ cwaves \
+ cynosure \
+ dangerball \
+ decayscreen \
+ deco \
+ deluxe \
+ demon \
+ discoball \
+ discrete \
+ distort \
+ dnalogo \
+ drift \
+ dymaxionmap \
+ endgame \
+ energystream \
+ engine \
+ epicycle \
+ eruption \
+ esper \
+ euler2d \
+ fadeplot \
+ fiberlamp \
+ filmleader \
+ fireworkx \
+ flame \
+ flipflop \
+ flipscreen3d \
+ flow \
+ fluidballs \
+ flyingtoasters \
+ fuzzyflakes \
+ galaxy \
+ gears \
+ geodesic \
+ geodesicgears \
+ gflux \
+ glblur \
+ glcells \
+ gleidescope \
+ glhanoi \
+ glknots \
+ glmatrix \
+ glplanet \
+ glschool \
+ glslideshow \
+ glsnake \
+ gltext \
+ goop \
+ grav \
+ greynetic \
+ halo \
+ helix \
+ hexadrop \
+ hexstrut \
+ hilbert \
+ hopalong \
+ hypertorus \
+ hypnowheel \
+ ifs \
+ imsmap \
+ interaggregate \
+ interference \
+ intermomentary \
+ jigglypuff \
+ jigsaw \
+ julia \
+ kaleidescope \
+ kaleidocycle \
+ klein \
+ kumppa \
+ lament \
+ lavalite \
+ loop \
+ m6502 \
+ maze \
+ maze3d \
+ memscroller \
+ menger \
+ metaballs \
+ mirrorblob \
+ moebius \
+ moebiusgears \
+ moire \
+ moire2 \
+ morph3d \
+ mountain \
+ munch \
+ nerverot \
+ noof \
+ noseguy \
+ peepers \
+ penetrate \
+ penrose \
+ petri \
+ piecewise \
+ pinion \
+ pipes \
+ polytopes \
+ pong \
+ popsquares \
+ projectiveplane \
+ providence \
+ pulsar \
+ pyro \
+ quasicrystal \
+ queens \
+ raverhoop \
+ razzledazzle \
+ rd-bomb \
+ ripples \
+ rocks \
+ romanboy \
+ rorschach \
+ rotzoomer \
+ rubik \
+ rubikblocks \
+ sballs \
+ shadebobs \
+ sierpinski \
+ sierpinski3d \
+ skytentacles \
+ slidescreen \
+ slip \
+ spheremonics \
+ splitflap \
+ splodesic \
+ spotlight \
+ squiral \
+ stairs \
+ stonerview \
+ strange \
+ substrate \
+ superquadrics \
+ surfaces \
+ swirl \
+ tangram \
+ tessellimage \
+ thornbird \
+ timetunnel \
+ topblock \
+ triangle \
+ tronbit \
+ truchet \
+ twang \
+ unknownpleasures \
+ vermiculate \
+ vfeedback \
+ vigilance \
+ voronoi \
+ wander \
+ whirlwindwarp \
+ winduprobot \
+ wormhole \
+ xanalogtv \
+ xflame \
+ xjack \
+ xlyap \
+ xmatrix \
+ xrayswarm \
+ xspirograph \
+ zoom \
+
+
+# These don't work well enough to turn on by default:
+#
+ANDROID_TODO= \
+ antinspect \
+ barcode \
+ carousel \
+ fliptext \
+ fontglide \
+ halftone \
+ juggler3d \
+ molecule \
+ pedal \
+ phosphor \
+ photopile \
+ polyominoes \
+ qix \
+ speedmine \
+ starfish \
+ starwars \
+ testx11 \
+ unicrud \
+
+
+# Download and resize images from jwz.org.
+# This saves us having to include 4MB of images in the tar file
+# that will only be used by a vast minority of people building
+# from source.
+# Android actually wants these to be 160x160 but our source is 200x150.
+
+URL = https://www.jwz.org/xscreensaver/screenshots/
+WGET = wget -q -U xscreensaver-build-android
+CVT = -thumbnail '150x150^' -gravity center -extent 150x150 \
+ \( +clone -alpha extract \
+ -draw 'fill black polygon 0,0 0,15 15,0 fill white circle 15,15 15,0' \
+ \( +clone -flip \) -compose Multiply -composite \
+ \( +clone -flop \) -compose Multiply -composite \
+ \) -alpha off -compose CopyOpacity -composite \
+ -colorspace sRGB \
+ -strip \
+ -quality 95 \
+ +dither -colors 128
+
+# If we are making the m6502 hack, create the header file for Android
+m6502.h::
+ @for h in $(ANDROID_HACKS) ; do \
+ if [ $${h} = "m6502" ] ; then \
+ echo "Making $${h} header ..."; \
+ ../hacks/m6502.sh ../hacks/m6502.h ../hacks/images/m6502/*.asm ; \
+ echo "Made $${h} header"; \
+ fi; \
+ done
+
+xscreensaver/res/drawable/%.png:
+ @\
+ FILE1=`echo "$@" | sed 's!^.*/\([^/]*\)\.png$$!\1.jpg!'` ; \
+ FILE2="$@" ; \
+ FILE1=`echo "$$FILE1" | sed s/rdbomb/rd-bomb/` ; \
+ FILE2=`echo "$$FILE2" | sed s/rd-bomb/rdbomb/` ; \
+ URL="$(URL)$$FILE1" ; \
+ echo "converting $$URL..." ; \
+ rm -f "$$FILE2" ; \
+ $(WGET) -O- "$$URL" | \
+ convert jpg:- $(CVT) "$$FILE2" ; \
+ if [ ! -s "$$FILE2" ]; then \
+ echo "$$FILE2 failed" >&2 ; \
+ exit 1 ; \
+ fi
+
+thumbs::
+ @for f in $(ANDROID_HACKS) $(ANDROID_TODO) ; do \
+ $(MAKE) xscreensaver/res/drawable/$$f.png ; \
+ done
+
+clean_thumbs::
+ @\
+ for f in $(ANDROID_HACKS) $(ANDROID_TODO) ; do \
+ rm -f xscreensaver/res/drawable/$$f.png ; \
+ done
+
+distclean:: clean_thumbs clean
+
+
+EXTRA_TARFILES = xscreensaver/res/drawable/thumbnail.png \
+
+echo_tarfiles:
+ @FILES=`find . $(EXTRA_TARFILES) \( \( \
+ -name .DS_Store \
+ -o -name '*~' \
+ -o -name '*.jks' \
+ -o -name '*.keystore' \
+ -o -name '*_dream.xml' \
+ -o -name '*_settings.xml' \
+ -o -name '*_wallpaper.xml' \
+ -o -name AndroidManifest.xml \
+ -o -name strings.xml \
+ -o -name settings.xml \
+ -o -name attrs.xml \
+ -o -name .gitignore \
+ -o -name .gradle \
+ -o -name drawable \
+ -o -name build \
+ -o -name gen \
+ -o -name libs \
+ -o -name obj \
+ \) -prune \) \
+ -o \( \( -type f -o -type l \) \
+ -print \) \
+ | sed 's@^\./@@' \
+ | sort` ; \
+ echo $$FILES
+
+images_png_h:
+ cd ../hacks/images && $(MAKE)
+
+run_check::
+ ../hacks/check-configs.pl --build-android $(ANDROID_HACKS)
+
+debug:: m6502.h run_check images_png_h
+ $(GRADLE) assembleDebug
+release:: m6502.h run_check images_png_h
+ export APP_ABI=all ; \
+ $(GRADLE) assembleRelease
+
+
+KEYSTORE = xscreensaver.jks
+$(KEYSTORE):
+ keytool -genkey -v -keystore $@ \
+ -alias xscreensaver -keyalg RSA -keysize 2048 -validity 10000
+
+APK_DIR = xscreensaver/build/outputs/apk/release/
+APK_UNSIGNED = $(APK_DIR)xscreensaver-release-unsigned.apk
+APK_UNALIGNED = $(APK_DIR)xscreensaver-release-unaligned.apk
+APK_SIGNED = $(APK_DIR)xscreensaver-release.apk
+
+ TOOLDIR = $(shell ls -d $$HOME/Library/Android/sdk/build-tools/* | tail -1)
+ ZIPALIGN = $(TOOLDIR)/zipalign
+JARSIGNER = jarsigner
+
+sign_release::
+ cp -p $(APK_UNSIGNED) $(APK_UNALIGNED)
+ $(JARSIGNER) -verbose -sigalg SHA1withRSA -digestalg SHA1 \
+ -keystore $(KEYSTORE) $(APK_UNALIGNED) xscreensaver
+ rm -f $(APK_SIGNED)
+ $(ZIPALIGN) -v 4 $(APK_UNALIGNED) $(APK_SIGNED)
+ rm -f $(APK_UNALIGNED)
+ $(JARSIGNER) -verify -verbose -certs $(APK_SIGNED)
+ @ls -lF $(APK_SIGNED)
+
+apk:: release
+ @\
+ VERS=`sed -n 's/[^0-9]*\([0-9]\.[0-9][^. ]*\).*/\1/p' ../utils/version.h` ; \
+ HEAD="xscreensaver-$$VERS" ; \
+ if [ ! -s $(APK_SIGNED) -o $(APK_UNSIGNED) -nt $(APK_SIGNED) ]; then \
+ $(MAKE) sign_release ; \
+ fi ; \
+ set -x ; \
+ cp -p $(APK_SIGNED) ../archive/$$HEAD.apk
+
+
+## #### Pare it down for faster debugging...
+#export APP_ABI = armeabi-v7a
+#export APP_ABI = x86
+#
+#export ANDROID_HACKS= \
+# $(ANDROID_BASE_HACKS) \
+# bsod \
+# apollonian \
+# engine \
+# dnalogo \
+# twang \
+# memscroller \
+# phosphor \
+# discoball \
+# cubetwist \
+# cubestack \
+# splodesic \
diff --git a/android/README b/android/README
new file mode 100644
index 0000000..2843296
--- /dev/null
+++ b/android/README
@@ -0,0 +1,189 @@
+
+This directory contains the Android-specific code for building xscreensaver.
+
+It is preliminary, and very much a work in progress.
+
+If you're messing with this, please let us know!
+
+ dennis@panaceasupplies.com
+ jwz@jwz.org
+
+
+To set up your Android development environment:
+
+ Install JDK 7 (http://www.oracle.com/technetwork/java/javase/downloads/)
+ Install Android Studio (http://developer.android.com/sdk/)
+ Install Android NDK (http://developer.android.com/ndk/downloads)
+ Rename or link the "android-ndk-*" directory to "ndk" inside your
+ $ANDROID_HOME (the "sdk/" directory that is the parent of
+ "build-tools/", etc.) That is, it should be "sdk/ndk/".
+
+ set $ANDROID_HOME to where your SDK is installed, or
+ set "sdk.dir" in the file local.properties.
+ On MacOS, the value you want is probably ~/Library/Android/sdk/
+ Also set "ndk.dir" in local.properties.
+
+To build:
+
+ ./configure
+ cd android
+ make
+
+ Hopefully an "xscreensaver-debug.apk" file will appear in
+ android/xscreensaver/build/outputs/apk/.
+
+ Load that onto your device and go to:
+ Settings / Display / Daydream
+ or just click on the XScreenSaver icon, which is a shortcut to that.
+
+ To create and configure an emulator image, use the GUI tool and and
+ give the emulator a name (say, "Nexus_5").
+
+ $ANDROID_HOME/sdk/tools/android avd
+
+ E.g.: Nexus 5, Android 5, Intel Atom x86_64, RAM 2048 VM 64,
+ storage 200, use host GPU.
+
+ Configuration options are in $HOME/.android/avd/*.avd/config.ini
+
+ To launch it:
+
+ $ANDROID_HOME/sdk/tools/emulator -avd Nexus_5
+
+ Warning! On my system at least, the emulator runs out of memory
+ when trying to display the Daydream page if all of the savers are
+ loaded. This is troubling. You can work around this by editing
+ your *.avd/config.ini and setting vm.heapSize=128; or by editing
+ android/Makefile and paring down the $ANDROID_HACKS list to a
+ smaller subset (60 or so with the default heapSize).
+
+
+ To load it into the currently-running emulator or device:
+ $ANDROID_HOME/platform-tools/adb install -r \
+ xscreensaver/build/outputs/apk/xscreensaver-debug.apk
+
+ Extremely verbose log output, including stack traces:
+ $ANDROID_HOME/platform-tools/adb logcat
+
+ Non-fatal log output for only this app:
+ $ANDROID_HOME/platform-tools/adb logcat \
+ -s xscreensaver:d AndroidRuntime:d libEGL:d
+
+ Note that sometimes "logcat" will just sit there forever saying
+ "waiting for device". This is because the emulator is a piece of
+ shit and sometimes decides to just randomly not service connections.
+ If you restart the emulator, and wait minutes for the whole damned
+ thing to boot up again, it will probably work next time. Probably.
+
+
+Directory structure:
+
+ Boilerplate for the Java version of "make":
+ *gradle*
+ *.properties
+ xscreensaver/*gradle*
+ xscreensaver/build.*
+ xscreensaver/*.properties
+
+ The other half of the Makefile:
+ xscreensaver/jni/*.mk
+
+ Source code:
+ xscreensaver/src/org/jwz/xscreensaver/*.java
+ xscreensaver/res/layout/*.xml
+
+ Other relevant source code is in ../jwxyz/ and ../hacks/.
+
+ Icons:
+ xscreensaver/res/drawable-ldpi/
+ xscreensaver/res/drawable-mdpi/
+ xscreensaver/res/drawable/
+
+ Files that we generate:
+
+ gen/function-table.h
+ xscreensaver/AndroidManifest.xml
+ xscreensaver/res/drawable/*.png
+ xscreensaver/res/values/settings.xml
+ xscreensaver/res/values/strings.xml
+ xscreensaver/res/xml/*.xml
+ xscreensaver/src/org/jwz/xscreensaver/gen/*.java
+
+ Other files generated as a part of the build process:
+
+ gen/
+ .gradle/
+ xscreensaver/build/
+ xscreensaver/build/outputs/apk/ -- app appears here
+ xscreensaver/jni/
+ xscreensaver/libs/
+ xscreensaver/obj/
+ xscreensaver/res/
+ xscreensaver/res/drawable/
+ xscreensaver/res/values/
+ xscreensaver/res/xml/
+ xscreensaver/src/org/jwz/xscreensaver/gen/
+
+When adding a new hack, edit android/Makefile, then "make clean" and "make".
+
+
+TODO list, and known bugs:
+
+ - See the top of ../jwxyz/jwxyz-gl.c for a low level TODO list,
+ but here's what's wrong with the savers from a high level view:
+
+ - Rotation is wonky (on some devices?)
+
+ - The Android emulator is a piece of shit and crashes all the time,
+ so it's possible that some of these work fine on real devices.
+ I don't actually have an Android device, so I have no idea.
+
+ - As mentioned above, the Android emulator runs out of memory if
+ more than about 60 of the Daydreams are installed with the default
+ heapSize. Again, I don't know if this is an issue on real devices.
+ I sure hope not.
+
+ - The code that attempts to grab a screen shot before the Daydream begins
+ doesn't work.
+
+ - When a saver exits abnormally, we catch the exception and attempt to
+ display the error message in a dialog. The catch works, but the dialog
+ box does not.
+
+ antinspect renders incorrectly
+ apple2 text, images into pixmaps doesn't work
+ barcode pixmaps
+ bsod pixmaps, XCopyArea problems
+ carousel images are corrupted
+ cityflow shading is wrong
+ endgame insanely slow
+ engine text
+ esper images don't load, no text
+ fliptext text
+ fontglide text
+ glblur grayscale instead of color
+ halftone XFillArc crash
+ jigglypuff incredibly slow
+ juggler3d blank
+ maze lines are not the same thickness (aliased?)
+ molecule blank
+ noseguy text; images
+ pacman images; launches really slowly
+ pedal polygons
+ phosphor text; pixmaps
+ photopile text; images don't display
+ polyhedra text
+ polyominoes pixmaps
+ qix polygons
+ queens insanely slow
+ sonar does icmp work?
+ speedmine polygons
+ splitflap super slow
+ spotlight blank
+ starfish polygons
+ starwars text
+ unicrud pixmaps
+ winduprobot dome is not transparent
+ xanalogtv pixmaps
+ xflame draws only upper left corner
+ xmatrix pretty slow
diff --git a/android/android.iml b/android/android.iml
new file mode 100644
index 0000000..7db6b23
--- /dev/null
+++ b/android/android.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/build.gradle b/android/build.gradle
new file mode 100644
index 0000000..648cc32
--- /dev/null
+++ b/android/build.gradle
@@ -0,0 +1,30 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ jcenter()
+ google()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.1.2'
+ }
+
+}
+
+allprojects {
+ repositories {
+ maven { url 'https://maven.google.com' }
+ jcenter()
+ google()
+ }
+}
+
+
+task clean(type: Delete) {
+ delete('./build')
+}
+
+task distClean(type: Delete) {
+ delete('./.gradle')
+}
+
+distClean.dependsOn clean
diff --git a/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/android/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0ac000f
--- /dev/null
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Feb 10 21:20:31 PST 2018
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-bin.zip
diff --git a/android/gradlew b/android/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/android/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/android/gradlew.bat b/android/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/android/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/local.properties b/android/local.properties
new file mode 100644
index 0000000..238a21c
--- /dev/null
+++ b/android/local.properties
@@ -0,0 +1,11 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+
+# location of the SDK. This is only used by Ant
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=/Users/jwz/Library/Android/sdk
+ndk.dir=/Users/jwz/Library/Android/sdk/ndk
diff --git a/android/screenhack-android.c b/android/screenhack-android.c
new file mode 100644
index 0000000..d5067ab
--- /dev/null
+++ b/android/screenhack-android.c
@@ -0,0 +1,209 @@
+/* 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.
+ *
+ * Utility functions related to the hacks/ APIs.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "screenhackI.h"
+#include "xlockmoreI.h"
+#include "textclient.h"
+
+#if defined(USE_IPHONE) || (HAVE_ANDROID)
+# include "jwzgles.h"
+#else
+# include
+#endif
+
+#ifndef isupper
+# define isupper(c) ((c) >= 'A' && (c) <= 'Z')
+#endif
+#ifndef _tolower
+# define _tolower(c) ((c) - 'A' + 'a')
+#endif
+
+Bool
+get_boolean_resource (Display *dpy, char *res_name, char *res_class)
+{
+ char *tmp, buf [100];
+ char *s = get_string_resource (dpy, res_name, res_class);
+ char *os = s;
+ if (! s) return 0;
+ for (tmp = buf; *s; s++)
+ *tmp++ = isupper (*s) ? _tolower (*s) : *s;
+ *tmp = 0;
+ free (os);
+
+ while (*buf &&
+ (buf[strlen(buf)-1] == ' ' ||
+ buf[strlen(buf)-1] == '\t'))
+ buf[strlen(buf)-1] = 0;
+
+ if (!strcmp (buf, "on") || !strcmp (buf, "true") || !strcmp (buf, "yes"))
+ return 1;
+ if (!strcmp (buf,"off") || !strcmp (buf, "false") || !strcmp (buf,"no"))
+ return 0;
+ fprintf (stderr, "%s: %s must be boolean, not %s.\n",
+ progname, res_name, buf);
+ return 0;
+}
+
+int
+get_integer_resource (Display *dpy, char *res_name, char *res_class)
+{
+ int val;
+ char c, *s = get_string_resource (dpy, res_name, res_class);
+ char *ss = s;
+ if (!s) return 0;
+
+ while (*ss && *ss <= ' ') ss++; /* skip whitespace */
+
+ if (ss[0] == '0' && (ss[1] == 'x' || ss[1] == 'X')) /* 0x: parse as hex */
+ {
+ if (1 == sscanf (ss+2, "%x %c", (unsigned int *) &val, &c))
+ {
+ free (s);
+ return val;
+ }
+ }
+ else /* else parse as dec */
+ {
+ /* Allow integer values to end in ".0". */
+ int L = strlen(ss);
+ if (L > 2 && ss[L-2] == '.' && ss[L-1] == '0')
+ ss[L-2] = 0;
+
+ if (1 == sscanf (ss, "%d %c", &val, &c))
+ {
+ free (s);
+ return val;
+ }
+ }
+
+ fprintf (stderr, "%s: %s must be an integer, not %s.\n",
+ progname, res_name, s);
+ free (s);
+ return 0;
+}
+
+double
+get_float_resource (Display *dpy, char *res_name, char *res_class)
+{
+ double val;
+ char c, *s = get_string_resource (dpy, res_name, res_class);
+ if (! s) return 0.0;
+ if (1 == sscanf (s, " %lf %c", &val, &c))
+ {
+ free (s);
+ return val;
+ }
+ fprintf (stderr, "%s: %s must be a float, not %s.\n",
+ progname, res_name, s);
+ free (s);
+ return 0.0;
+}
+
+
+char *
+textclient_mobile_date_string (void)
+{
+ struct utsname uts;
+ if (uname (&uts) < 0)
+ return strdup("uname() failed");
+ else
+ {
+ time_t now = time ((time_t *) 0);
+ char *ts = ctime (&now);
+ char *buf, *s;
+ if ((s = strchr(uts.nodename, '.')))
+ *s = 0;
+ buf = (char *) malloc(strlen(uts.machine) +
+ strlen(uts.sysname) +
+ strlen(uts.release) +
+ strlen(ts) + 10);
+ sprintf (buf, "%s %s %s\n%s", uts.machine, uts.sysname, uts.release, ts);
+ return buf;
+ }
+}
+
+
+/* used by the OpenGL screen savers
+ */
+
+/* Does nothing - prepareContext already did the work.
+ */
+void
+glXMakeCurrent (Display *dpy, Window window, GLXContext context)
+{
+}
+
+
+/* clear away any lingering error codes */
+void
+clear_gl_error (void)
+{
+ while (glGetError() != GL_NO_ERROR)
+ ;
+}
+
+
+// needs to be implemented in Android...
+/* Copy the back buffer to the front buffer.
+ */
+void
+glXSwapBuffers (Display *dpy, Window window)
+{
+}
+
+
+/* Called by OpenGL savers using the XLockmore API.
+ */
+GLXContext *
+init_GL (ModeInfo *mi)
+{
+ // Window win = mi->window;
+
+ // Caller expects a pointer to an opaque struct... which it dereferences.
+ // Don't ask me, it's historical...
+ static int blort = -1;
+ return (void *) &blort;
+}
+
+/* report a GL error. */
+void
+check_gl_error (const char *type)
+{
+ char buf[100];
+ GLenum i;
+ const char *e;
+ switch ((i = glGetError())) {
+ case GL_NO_ERROR: return;
+ case GL_INVALID_ENUM: e = "invalid enum"; break;
+ case GL_INVALID_VALUE: e = "invalid value"; break;
+ case GL_INVALID_OPERATION: e = "invalid operation"; break;
+ case GL_STACK_OVERFLOW: e = "stack overflow"; break;
+ case GL_STACK_UNDERFLOW: e = "stack underflow"; break;
+ case GL_OUT_OF_MEMORY: e = "out of memory"; break;
+#ifdef GL_TABLE_TOO_LARGE_EXT
+ case GL_TABLE_TOO_LARGE_EXT: e = "table too large"; break;
+#endif
+#ifdef GL_TEXTURE_TOO_LARGE_EXT
+ case GL_TEXTURE_TOO_LARGE_EXT: e = "texture too large"; break;
+#endif
+ default:
+ e = buf; sprintf (buf, "unknown GL error %d", (int) i); break;
+ }
+ __android_log_write(ANDROID_LOG_ERROR, "xscreensaver", e);
+}
diff --git a/android/settings.gradle b/android/settings.gradle
new file mode 100644
index 0000000..5fc665b
--- /dev/null
+++ b/android/settings.gradle
@@ -0,0 +1 @@
+include ':xscreensaver'
diff --git a/android/xscreensaver/.idea/caches/build_file_checksums.ser b/android/xscreensaver/.idea/caches/build_file_checksums.ser
new file mode 100644
index 0000000..bc8f00c
Binary files /dev/null and b/android/xscreensaver/.idea/caches/build_file_checksums.ser differ
diff --git a/android/xscreensaver/.idea/codeStyles/Project.xml b/android/xscreensaver/.idea/codeStyles/Project.xml
new file mode 100644
index 0000000..30aa626
--- /dev/null
+++ b/android/xscreensaver/.idea/codeStyles/Project.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/compiler.xml b/android/xscreensaver/.idea/compiler.xml
new file mode 100644
index 0000000..9a8b7e5
--- /dev/null
+++ b/android/xscreensaver/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/gradle.xml b/android/xscreensaver/.idea/gradle.xml
new file mode 100644
index 0000000..3ac097a
--- /dev/null
+++ b/android/xscreensaver/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__android_arch_core_common_1_1_0_jar.xml b/android/xscreensaver/.idea/libraries/Gradle__android_arch_core_common_1_1_0_jar.xml
new file mode 100644
index 0000000..9869803
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__android_arch_core_common_1_1_0_jar.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__android_arch_core_runtime_1_1_0.xml b/android/xscreensaver/.idea/libraries/Gradle__android_arch_core_runtime_1_1_0.xml
new file mode 100644
index 0000000..03529f7
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__android_arch_core_runtime_1_1_0.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_common_1_1_0_jar.xml b/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_common_1_1_0_jar.xml
new file mode 100644
index 0000000..29c0049
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_common_1_1_0_jar.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_livedata_core_1_1_0.xml b/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_livedata_core_1_1_0.xml
new file mode 100644
index 0000000..c43180e
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_livedata_core_1_1_0.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_runtime_1_1_0.xml b/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_runtime_1_1_0.xml
new file mode 100644
index 0000000..6031fb9
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_runtime_1_1_0.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_viewmodel_1_1_0.xml b/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_viewmodel_1_1_0.xml
new file mode 100644
index 0000000..11a24fb
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__android_arch_lifecycle_viewmodel_1_1_0.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_annotations_27_1_1_jar.xml b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_annotations_27_1_1_jar.xml
new file mode 100644
index 0000000..1517ad9
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_annotations_27_1_1_jar.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_compat_27_1_1.xml b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_compat_27_1_1.xml
new file mode 100644
index 0000000..fba52e8
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_compat_27_1_1.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_core_ui_27_1_1.xml b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_core_ui_27_1_1.xml
new file mode 100644
index 0000000..3ccece0
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_core_ui_27_1_1.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_core_utils_27_1_1.xml b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_core_utils_27_1_1.xml
new file mode 100644
index 0000000..69d23f2
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_core_utils_27_1_1.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_fragment_27_1_1.xml b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_fragment_27_1_1.xml
new file mode 100644
index 0000000..8eb356f
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_fragment_27_1_1.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_media_compat_27_1_1.xml b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_media_compat_27_1_1.xml
new file mode 100644
index 0000000..c577eba
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_media_compat_27_1_1.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_v4_27_1_1.xml b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_v4_27_1_1.xml
new file mode 100644
index 0000000..10d3108
--- /dev/null
+++ b/android/xscreensaver/.idea/libraries/Gradle__com_android_support_support_v4_27_1_1.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/misc.xml b/android/xscreensaver/.idea/misc.xml
new file mode 100644
index 0000000..c0f68ed
--- /dev/null
+++ b/android/xscreensaver/.idea/misc.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/modules.xml b/android/xscreensaver/.idea/modules.xml
new file mode 100644
index 0000000..33191fe
--- /dev/null
+++ b/android/xscreensaver/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/runConfigurations.xml b/android/xscreensaver/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/android/xscreensaver/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/.idea/workspace.xml b/android/xscreensaver/.idea/workspace.xml
new file mode 100644
index 0000000..1dedd64
--- /dev/null
+++ b/android/xscreensaver/.idea/workspace.xml
@@ -0,0 +1,2227 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1475536195508
+
+
+ 1475536195508
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/android/xscreensaver/assets/fonts/OCRAStd.otf b/android/xscreensaver/assets/fonts/OCRAStd.otf
new file mode 120000
index 0000000..6259345
--- /dev/null
+++ b/android/xscreensaver/assets/fonts/OCRAStd.otf
@@ -0,0 +1 @@
+../../../../OSX/OCRAStd.otf
\ No newline at end of file
diff --git a/android/xscreensaver/assets/fonts/PxPlus_IBM_VGA8.ttf b/android/xscreensaver/assets/fonts/PxPlus_IBM_VGA8.ttf
new file mode 120000
index 0000000..184d654
--- /dev/null
+++ b/android/xscreensaver/assets/fonts/PxPlus_IBM_VGA8.ttf
@@ -0,0 +1 @@
+../../../../OSX/PxPlus_IBM_VGA8.ttf
\ No newline at end of file
diff --git a/android/xscreensaver/assets/fonts/YearlReg.ttf b/android/xscreensaver/assets/fonts/YearlReg.ttf
new file mode 120000
index 0000000..8977be4
--- /dev/null
+++ b/android/xscreensaver/assets/fonts/YearlReg.ttf
@@ -0,0 +1 @@
+../../../../OSX/YearlReg.ttf
\ No newline at end of file
diff --git a/android/xscreensaver/build.gradle b/android/xscreensaver/build.gradle
new file mode 100644
index 0000000..f0ff9a3
--- /dev/null
+++ b/android/xscreensaver/build.gradle
@@ -0,0 +1,109 @@
+apply plugin: 'android'
+
+dependencies {
+ compile fileTree(include: '*.jar', dir: 'libs')
+ compile "com.android.support:support-v4:27.1.1"
+}
+
+android {
+ compileSdkVersion 27
+ buildToolsVersion "27.0.3"
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_7
+ targetCompatibility JavaVersion.VERSION_1_7
+ }
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ // jniLibs.srcDirs = ['jni']
+ jniLibs.srcDirs = ['libs']
+ jni.srcDirs = [] // disable automatic ndk-build call
+ }
+
+ // Move the tests to tests/java, tests/res, etc...
+ androidTest.setRoot('tests')
+
+ // Move the build types to build-types/
+ // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
+ // This moves them out of them default location under src//... which would
+ // conflict with src/ being used by the main source set.
+ // Adding new build types or product flavors should be accompanied
+ // by a similar customization.
+ debug.setRoot('build-types/debug')
+ release.setRoot('build-types/release')
+ }
+ project.ext.versionCodes = ['armeabi': 1, 'armeabi-v7a': 2, 'arm64-v8a': 3, 'mips': 5, 'mips64': 6, 'x86': 8, 'x86_64': 9]
+ //versionCode digit for each supported ABI, with 64bit>32bit and x86>armeabi-*
+
+
+ // put ndk-build in build's path, or replace below with its full path
+ task ndkBuild(type: Exec) {
+ Properties properties = new Properties()
+ properties.load(project.rootProject.file('local.properties').newDataInputStream())
+ def ndkDir = properties.getProperty('ndk.dir')
+ commandLine "$ndkDir/ndk-build", '-C', file('jni').absolutePath,
+ '-j' + Runtime.getRuntime().availableProcessors().toString()
+ }
+
+ // generate files early in the process
+ task perlBuild(type: Exec) {
+ commandLine 'sh', '-c',
+ 'cd ..; ../hacks/check-configs.pl --build-android $ANDROID_HACKS'
+ }
+
+ task perlClean(type: Delete) {
+ delete('../gen')
+ delete('res/values')
+ delete('res/xml')
+ delete('src/org/jwz/xscreensaver/gen')
+ delete('AndroidManifest.xml')
+ }
+
+ task objlibClean(type: Delete) {
+ delete('./build')
+ delete('./libs')
+ delete('./obj')
+ }
+
+ task downloadNeededDrawables(type: Exec) {
+ commandLine 'sh', '-c',
+ 'cd ../ ; \
+ for f in $ANDROID_HACKS; do \
+ f=`echo "$f" | sed s/rd-bomb/rdbomb/` ; \
+ make -s xscreensaver/res/drawable/$f.png ; \
+ done'
+ }
+ preBuild.dependsOn downloadNeededDrawables
+
+ preBuild.dependsOn perlBuild
+
+ clean.dependsOn perlClean
+ clean.dependsOn objlibClean
+
+ tasks.withType(JavaCompile) {
+ options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+ }
+
+ tasks.withType(JavaCompile) {
+ compileTask -> compileTask.dependsOn ndkBuild
+ }
+
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 27
+ }
+ productFlavors {
+ }
+ buildTypes {
+ debug {
+ jniDebuggable true
+ }
+ }
+}
diff --git a/android/xscreensaver/build.xml b/android/xscreensaver/build.xml
new file mode 100644
index 0000000..abda7fe
--- /dev/null
+++ b/android/xscreensaver/build.xml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/xscreensaver/gradle/wrapper/gradle-wrapper.jar b/android/xscreensaver/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/android/xscreensaver/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/android/xscreensaver/gradle/wrapper/gradle-wrapper.properties b/android/xscreensaver/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..3524910
--- /dev/null
+++ b/android/xscreensaver/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sat Feb 10 21:20:31 PST 2018
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/android/xscreensaver/gradlew b/android/xscreensaver/gradlew
new file mode 100644
index 0000000..9d82f78
--- /dev/null
+++ b/android/xscreensaver/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/android/xscreensaver/gradlew.bat b/android/xscreensaver/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/android/xscreensaver/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/android/xscreensaver/jni/Android.mk b/android/xscreensaver/jni/Android.mk
new file mode 100644
index 0000000..1e20a83
--- /dev/null
+++ b/android/xscreensaver/jni/Android.mk
@@ -0,0 +1,199 @@
+LOCAL_PATH := $(call my-dir)/../../..
+
+# -Wnested-externs would also be here, but for Android unistd.h.
+SHARED_CFLAGS = \
+ -std=c99 \
+ -Wall \
+ -Wstrict-prototypes \
+ -Wmissing-prototypes \
+ -DGL_GLEXT_PROTOTYPES \
+ -DSTANDALONE=1 \
+ -DHAVE_ANDROID=1 \
+ -DHAVE_GL=1 \
+ -DHAVE_JWXYZ=1 \
+ -DJWXYZ_GL=1 \
+ -DJWXYZ_IMAGE=1 \
+ -DHAVE_JWZGLES=1 \
+ -DHAVE_XUTF8DRAWSTRING=1 \
+ -DHAVE_GLBINDTEXTURE=1 \
+ -DHAVE_UNISTD_H=1 \
+ -DHAVE_INTTYPES_H=1 \
+ -DHAVE_UNAME=1 \
+ -DHAVE_UTIL_H=1 \
+ -DGETTIMEOFDAY_TWO_ARGS=1 \
+ -DHAVE_ICMP=1 \
+ -DHAVE_PTHREAD=1 \
+
+SHARED_C_INCLUDES = \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/android \
+ $(LOCAL_PATH)/utils \
+ $(LOCAL_PATH)/jwxyz \
+ $(LOCAL_PATH)/hacks \
+ $(LOCAL_PATH)/hacks/glx \
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := xscreensaver-gl
+
+LOCAL_SRC_FILES := \
+ android/screenhack-android.c \
+ hacks/glx/dropshadow.c \
+ hacks/glx/chessmodels.c \
+ hacks/glx/fps-gl.c \
+ hacks/glx/gltrackball.c \
+ hacks/glx/glut_stroke.c \
+ hacks/glx/glut_swidth.c \
+ hacks/glx/grab-ximage.c \
+ hacks/glx/marching.c \
+ hacks/glx/normals.c \
+ hacks/glx/rotator.c \
+ hacks/glx/sphere.c \
+ hacks/glx/texfont.c \
+ hacks/glx/trackball.c \
+ hacks/glx/tube.c \
+
+# Some savers occupy more than one source file:
+LOCAL_SRC_FILES += \
+ hacks/glx/b_draw.c \
+ hacks/glx/b_lockglue.c \
+ hacks/glx/b_sphere.c \
+ hacks/glx/buildlwo.c \
+ hacks/glx/companion_quad.c \
+ hacks/glx/companion_disc.c \
+ hacks/glx/companion_heart.c \
+ hacks/glx/cow_face.c \
+ hacks/glx/cow_hide.c \
+ hacks/glx/cow_hoofs.c \
+ hacks/glx/cow_horns.c \
+ hacks/glx/cow_tail.c \
+ hacks/glx/cow_udder.c \
+ hacks/glx/dolphin.c \
+ hacks/glx/dymaxionmap-coords.c \
+ hacks/glx/gllist.c \
+ hacks/glx/glschool_alg.c \
+ hacks/glx/glschool_gl.c \
+ hacks/glx/involute.c \
+ hacks/glx/lament_model.c \
+ hacks/glx/pipeobjs.c \
+ hacks/glx/quickhull.c \
+ hacks/glx/robot.c \
+ hacks/glx/robot-wireframe.c \
+ hacks/glx/polyhedra-gl.c \
+ hacks/glx/s1_1.c \
+ hacks/glx/s1_2.c \
+ hacks/glx/s1_3.c \
+ hacks/glx/s1_4.c \
+ hacks/glx/s1_5.c \
+ hacks/glx/s1_6.c \
+ hacks/glx/s1_b.c \
+ hacks/glx/seccam.c \
+ hacks/glx/shark.c \
+ hacks/glx/ships.c \
+ hacks/glx/sonar-sim.c \
+ hacks/glx/sonar-icmp.c \
+ hacks/glx/splitflap_obj.c \
+ hacks/glx/sproingiewrap.c \
+ hacks/glx/stonerview-move.c \
+ hacks/glx/stonerview-osc.c \
+ hacks/glx/stonerview-view.c \
+ hacks/glx/swim.c \
+ hacks/glx/tangram_shapes.c \
+ hacks/glx/teapot.c \
+ hacks/glx/toast.c \
+ hacks/glx/toast2.c \
+ hacks/glx/toaster.c \
+ hacks/glx/toaster_base.c \
+ hacks/glx/toaster_handle.c \
+ hacks/glx/toaster_handle2.c \
+ hacks/glx/toaster_jet.c \
+ hacks/glx/toaster_knob.c \
+ hacks/glx/toaster_slots.c \
+ hacks/glx/toaster_wing.c \
+ hacks/glx/tronbit_idle1.c \
+ hacks/glx/tronbit_idle2.c \
+ hacks/glx/tronbit_no.c \
+ hacks/glx/tronbit_yes.c \
+ hacks/glx/tunnel_draw.c \
+ hacks/glx/whale.c \
+
+# The source files of the currently active GL hacks:
+LOCAL_SRC_FILES += $(shell \
+ for f in $$ANDROID_HACKS ; do \
+ if [ "$$f" = "companioncube" ]; then f="companion"; fi ; \
+ if [ -f "../../../hacks/glx/$$f.c" ]; then \
+ echo "hacks/glx/$$f.c" ; \
+ fi ; \
+ done )
+
+LOCAL_C_INCLUDES := $(SHARED_C_INCLUDES)
+LOCAL_CFLAGS += $(SHARED_CFLAGS) -DUSE_GL
+
+include $(BUILD_STATIC_LIBRARY)
+
+##############################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := xscreensaver
+
+LOCAL_STATIC_LIBRARIES := xscreensaver-gl
+
+# The base framework files:
+LOCAL_SRC_FILES := \
+ jwxyz/jwxyz-android.c \
+ jwxyz/jwxyz-common.c \
+ jwxyz/jwxyz-gl.c \
+ jwxyz/jwxyz-image.c \
+ jwxyz/jwxyz-timers.c \
+ jwxyz/jwzgles.c \
+
+# Utilities used by the hacks:
+LOCAL_SRC_FILES += \
+ hacks/analogtv.c \
+ hacks/delaunay.c \
+ hacks/fps.c \
+ hacks/xlockmore.c \
+ hacks/ximage-loader.c \
+ utils/async_netdb.c \
+ utils/aligned_malloc.c \
+ utils/colorbars.c \
+ utils/colors.c \
+ utils/erase.c \
+ utils/font-retry.c \
+ utils/grabclient.c \
+ utils/hsv.c \
+ utils/logo.c \
+ utils/minixpm.c \
+ utils/pow2.c \
+ utils/resources.c \
+ utils/spline.c \
+ utils/textclient-mobile.c \
+ utils/thread_util.c \
+ utils/usleep.c \
+ utils/utf8wc.c \
+ utils/xft.c \
+ utils/xshm.c \
+ utils/yarandom.c \
+
+# The source files of the currently active Xlib hacks:
+LOCAL_SRC_FILES += $(shell \
+ for f in $$ANDROID_HACKS ; do \
+ if [ -f "../../../hacks/$$f.c" ]; then \
+ echo "hacks/$$f.c" ; \
+ fi ; \
+ done )
+
+# Some savers occupy more than one source file:
+LOCAL_SRC_FILES += \
+ hacks/apple2-main.c \
+ hacks/asm6502.c \
+ hacks/pacman_ai.c \
+ hacks/pacman_level.c \
+
+LOCAL_LDLIBS := -lGLESv1_CM -ldl -llog -lEGL -latomic -landroid -ljnigraphics
+
+LOCAL_C_INCLUDES := $(SHARED_C_INCLUDES)
+LOCAL_CFLAGS += $(SHARED_CFLAGS)
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/android/xscreensaver/jni/Application.mk b/android/xscreensaver/jni/Application.mk
new file mode 100644
index 0000000..d3f5436
--- /dev/null
+++ b/android/xscreensaver/jni/Application.mk
@@ -0,0 +1,7 @@
+# Get this value from android/Makefile
+APP_ABI := $(shell echo $$APP_ABI)
+APP_STL := stlport_static
+APP_PLATFORM := android-14
+# ^^ APP_PLATFORM is minimum API version supported
+# https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md#target-api-set-higher-than-device-api
+
diff --git a/android/xscreensaver/local.properties b/android/xscreensaver/local.properties
new file mode 100644
index 0000000..d530e0d
--- /dev/null
+++ b/android/xscreensaver/local.properties
@@ -0,0 +1,11 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+#Mon Oct 03 16:09:56 PDT 2016
+sdk.dir=/Users/jwz/Library/Android/sdk
diff --git a/android/xscreensaver/project.properties b/android/xscreensaver/project.properties
new file mode 100644
index 0000000..1e1e7a3
--- /dev/null
+++ b/android/xscreensaver/project.properties
@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+android.library.reference.1=
+# Project target.
+target=android-19
diff --git a/android/xscreensaver/res/drawable-ldpi/icon.png b/android/xscreensaver/res/drawable-ldpi/icon.png
new file mode 100644
index 0000000..c4d1934
Binary files /dev/null and b/android/xscreensaver/res/drawable-ldpi/icon.png differ
diff --git a/android/xscreensaver/res/drawable-mdpi/icon.png b/android/xscreensaver/res/drawable-mdpi/icon.png
new file mode 100644
index 0000000..1e14e4b
Binary files /dev/null and b/android/xscreensaver/res/drawable-mdpi/icon.png differ
diff --git a/android/xscreensaver/res/drawable/thumbnail.png b/android/xscreensaver/res/drawable/thumbnail.png
new file mode 100644
index 0000000..a86c16c
Binary files /dev/null and b/android/xscreensaver/res/drawable/thumbnail.png differ
diff --git a/android/xscreensaver/res/layout-land/activity_xscreensaver.xml b/android/xscreensaver/res/layout-land/activity_xscreensaver.xml
new file mode 100644
index 0000000..d9094b9
--- /dev/null
+++ b/android/xscreensaver/res/layout-land/activity_xscreensaver.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/xscreensaver/res/layout/activity_tv_xscreensaver.xml b/android/xscreensaver/res/layout/activity_tv_xscreensaver.xml
new file mode 100644
index 0000000..4c658bd
--- /dev/null
+++ b/android/xscreensaver/res/layout/activity_tv_xscreensaver.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/xscreensaver/res/layout/activity_xscreensaver.xml b/android/xscreensaver/res/layout/activity_xscreensaver.xml
new file mode 100644
index 0000000..88372e6
--- /dev/null
+++ b/android/xscreensaver/res/layout/activity_xscreensaver.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/xscreensaver/res/layout/main.xml b/android/xscreensaver/res/layout/main.xml
new file mode 100644
index 0000000..4361cfe
--- /dev/null
+++ b/android/xscreensaver/res/layout/main.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/android/xscreensaver/res/layout/preference_blurb.xml b/android/xscreensaver/res/layout/preference_blurb.xml
new file mode 100644
index 0000000..66e6d82
--- /dev/null
+++ b/android/xscreensaver/res/layout/preference_blurb.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/xscreensaver/res/layout/slider_preference.xml b/android/xscreensaver/res/layout/slider_preference.xml
new file mode 100644
index 0000000..69647ff
--- /dev/null
+++ b/android/xscreensaver/res/layout/slider_preference.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/Activity.java b/android/xscreensaver/src/org/jwz/xscreensaver/Activity.java
new file mode 100644
index 0000000..ac0ab4c
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/Activity.java
@@ -0,0 +1,169 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * xscreensaver, Copyright (c) 2016 Jamie Zawinski
+ * and Dennis Sheil
+ *
+ * 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.
+ *
+ * This is the XScreenSaver "application" that just brings up the
+ * Live Wallpaper preferences.
+ */
+
+package org.jwz.xscreensaver;
+
+import android.app.WallpaperManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.provider.Settings;
+import android.Manifest;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.os.Build;
+import android.content.pm.PackageManager;
+
+public class Activity extends android.app.Activity
+ implements View.OnClickListener {
+
+ private boolean wallpaperButtonClicked, daydreamButtonClicked;
+ private final static int MY_REQ_READ_EXTERNAL_STORAGE = 271828;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // openList();
+ setContentView(R.layout.activity_xscreensaver);
+ wallpaperButtonClicked = false;
+ daydreamButtonClicked = false;
+
+ findViewById(R.id.apply_wallpaper).setOnClickListener(this);
+ findViewById(R.id.apply_daydream).setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.apply_wallpaper:
+ wallpaperButtonClicked();
+ break;
+ case R.id.apply_daydream:
+ daydreamButtonClicked();
+ break;
+ }
+ }
+
+ // synchronized when dealing with wallpaper state - perhaps can
+ // narrow down more
+ private synchronized void withProceed() {
+ if (daydreamButtonClicked) {
+ String action;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ action = Settings.ACTION_DREAM_SETTINGS;
+ } else {
+ action = Settings.ACTION_DISPLAY_SETTINGS;
+ }
+ startActivity(new Intent(action));
+ } else if (wallpaperButtonClicked) {
+ startActivity(new Intent(WallpaperManager.ACTION_LIVE_WALLPAPER_CHOOSER));
+ }
+ }
+
+ private void wallpaperButtonClicked() {
+ wallpaperButtonClicked = true;
+ checkPermission();
+ }
+
+ private void daydreamButtonClicked() {
+ daydreamButtonClicked = true;
+ checkPermission();
+ }
+
+ void checkPermission() {
+ // RES introduced in API 16
+ String permission = Manifest.permission.READ_EXTERNAL_STORAGE;
+ if (havePermission(permission)) {
+ withProceed();
+ } else {
+ noPermission(permission);
+ }
+ }
+
+ private void noPermission(String permission) {
+ int myRequestCode;
+ myRequestCode = MY_REQ_READ_EXTERNAL_STORAGE;
+
+ if (permissionsDeniedRationale(permission)) {
+ showDeniedRationale();
+ } else {
+ requestPermission(permission, myRequestCode);
+ }
+ }
+
+ private boolean permissionsDeniedRationale(String permission) {
+ boolean rationale = ActivityCompat.shouldShowRequestPermissionRationale(this,
+ permission);
+ return rationale;
+ }
+
+ private void requestPermission(String permission, int myRequestCode) {
+ ActivityCompat.requestPermissions(this,
+ new String[]{permission},
+ myRequestCode);
+
+ // myRequestCode is an app-defined int constant.
+ // The callback method gets the result of the request.
+ }
+
+ // TODO: This method should be asynchronous, and not block the thread
+ private void showDeniedRationale() {
+ withProceed();
+ }
+
+ boolean havePermission(String permission) {
+
+ if (Build.VERSION.SDK_INT < 16) {
+ return true;
+ }
+
+ if (permissionGranted(permission)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean permissionGranted(String permission) {
+ boolean check = ContextCompat.checkSelfPermission(this, permission) ==
+ PackageManager.PERMISSION_GRANTED;
+ return check;
+ }
+
+ public void proceedIfPermissionGranted(int[] grantResults) {
+
+ // If request is cancelled, the result arrays are empty.
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ withProceed();
+ } else if (grantResults.length > 0) {
+ withProceed();
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String permissions[], int[] grantResults) {
+ switch (requestCode) {
+ case MY_REQ_READ_EXTERNAL_STORAGE:
+ proceedIfPermissionGranted(grantResults);
+ }
+ }
+
+}
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/App.java b/android/xscreensaver/src/org/jwz/xscreensaver/App.java
new file mode 100644
index 0000000..3d39788
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/App.java
@@ -0,0 +1,22 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * xscreensaver, Copyright (c) 2016 Jamie Zawinski
+ * and Dennis Sheil
+ *
+ * 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.
+ */
+
+package org.jwz.xscreensaver;
+
+import android.app.Application;
+
+public class App extends Application {
+ public App() {
+ super();
+ }
+}
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/Daydream.java b/android/xscreensaver/src/org/jwz/xscreensaver/Daydream.java
new file mode 100644
index 0000000..372af95
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/Daydream.java
@@ -0,0 +1,269 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * xscreensaver, Copyright (c) 2016-2017 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 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);
+ }
+ }
+ }
+}
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/Settings.java b/android/xscreensaver/src/org/jwz/xscreensaver/Settings.java
new file mode 100644
index 0000000..17bac0f
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/Settings.java
@@ -0,0 +1,179 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * xscreensaver, Copyright (c) 2016 Jamie Zawinski
+ * and Dennis Sheil
+ *
+ * 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 preferences panel.
+ *
+ * The only reason the subclasses of this class exist is so that we know
+ * which "_settings.xml" to read -- we extract the base name from self's
+ * class.
+ *
+ * project/xscreensaver/res/xml/SAVER_dream.xml refers to it as
+ * android:settingsActivity="SAVER_Settings". If there was some way
+ * to pass an argument from the XML into here, or to otherwise detect
+ * which Dream was instantiating this Settings, we wouldn't need those
+ * hundreds of Settings subclasses.
+ */
+
+package org.jwz.xscreensaver;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+
+import android.content.SharedPreferences;
+import android.preference.PreferenceActivity;
+import android.preference.Preference;
+import android.preference.ListPreference;
+import android.preference.EditTextPreference;
+import android.preference.CheckBoxPreference;
+import org.jwz.xscreensaver.SliderPreference;
+
+import org.jwz.xscreensaver.R;
+import java.util.Map;
+import java.lang.reflect.Field;
+
+public abstract class Settings extends PreferenceActivity
+ implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ @Override
+ protected void onCreate (Bundle icicle) {
+ super.onCreate (icicle);
+
+ // Extract the saver name from e.g. "BouncingCowSettings"
+ String name = this.getClass().getSimpleName();
+ String tail = "Settings";
+ if (name.endsWith(tail))
+ name = name.substring (0, name.length() - tail.length());
+ name = name.toLowerCase();
+
+ // #### All of these have been deprecated:
+ // getPreferenceManager()
+ // addPreferencesFromResource(int)
+ // findPreference(CharSequence)
+
+ getPreferenceManager().setSharedPreferencesName (name);
+
+ // read R.xml.SAVER_settings dynamically
+ int res = -1;
+ String pref_class = name + "_settings";
+ try { res = R.xml.class.getDeclaredField(pref_class).getInt (null); }
+ catch (Exception e) { }
+ if (res != -1)
+ addPreferencesFromResource (res);
+
+ final int res_final = res;
+
+ SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+ prefs.registerOnSharedPreferenceChangeListener (this);
+ updateAllPrefsSummaries (prefs);
+
+ // Find the "Reset to defaults" button and install a click handler on it.
+ //
+ Preference reset = findPreference (name + "_reset");
+ reset.setOnPreferenceClickListener(
+ new Preference.OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+
+ SharedPreferences prefs =
+ getPreferenceManager().getSharedPreferences();
+
+ // Wipe everything from the preferences hash, then reload defaults.
+ prefs.edit().clear().commit();
+ getPreferenceScreen().removeAll();
+ addPreferencesFromResource (res_final);
+
+ // I guess we need to re-get this after the removeAll?
+ prefs = getPreferenceManager().getSharedPreferences();
+
+ // But now we need to iterate over every Preference widget and
+ // push the new value down into it. If you think this all looks
+ // ridiculously non-object-oriented and completely insane, that's
+ // because it is.
+
+ Map keys = prefs.getAll();
+ for (Map.Entry entry : keys.entrySet()) {
+ String key = entry.getKey();
+ String val = String.valueOf (entry.getValue());
+
+ Preference pref = findPreference (key);
+ if (pref instanceof ListPreference) {
+ ((ListPreference) pref).setValue (prefs.getString (key, ""));
+ } else if (pref instanceof SliderPreference) {
+ ((SliderPreference) pref).setValue (prefs.getFloat (key, 0));
+ } else if (pref instanceof EditTextPreference) {
+ ((EditTextPreference) pref).setText (prefs.getString (key, ""));
+ } else if (pref instanceof CheckBoxPreference) {
+ ((CheckBoxPreference) pref).setChecked (
+ prefs.getBoolean (key,false));
+ }
+
+ updatePrefsSummary (prefs, pref);
+ }
+ return true;
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+ prefs.registerOnSharedPreferenceChangeListener (this);
+ updateAllPrefsSummaries(prefs);
+ }
+
+ @Override
+ protected void onPause() {
+ getPreferenceManager().getSharedPreferences().
+ unregisterOnSharedPreferenceChangeListener(this);
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ getPreferenceManager().getSharedPreferences().
+ unregisterOnSharedPreferenceChangeListener(this);
+ super.onDestroy();
+ }
+
+ public void onSharedPreferenceChanged (SharedPreferences sharedPreferences,
+ String key) {
+ updatePrefsSummary(sharedPreferences, findPreference(key));
+ }
+
+ protected void updatePrefsSummary(SharedPreferences sharedPreferences,
+ Preference pref) {
+ if (pref == null)
+ return;
+
+ if (pref instanceof ListPreference) {
+ pref.setTitle (((ListPreference) pref).getEntry());
+ } else if (pref instanceof SliderPreference) {
+ float v = ((SliderPreference) pref).getValue();
+ int i = (int) Math.floor (v);
+ if (v == i)
+ pref.setSummary (String.valueOf (i));
+ else
+ pref.setSummary (String.valueOf (v));
+ } else if (pref instanceof EditTextPreference) {
+ pref.setSummary (((EditTextPreference) pref).getText());
+ }
+ }
+
+ protected void updateAllPrefsSummaries(SharedPreferences prefs) {
+
+ Map keys = prefs.getAll();
+ for (Map.Entry entry : keys.entrySet()) {
+ updatePrefsSummary (prefs, findPreference (entry.getKey()));
+ }
+ }
+}
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/SliderPreference.java b/android/xscreensaver/src/org/jwz/xscreensaver/SliderPreference.java
new file mode 100644
index 0000000..c1a1a1d
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/SliderPreference.java
@@ -0,0 +1,160 @@
+/* -*- 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.
+ *
+ * A numeric preference as a slider, inline in the preferences list.
+ * XML options include:
+ *
+ * low, high (floats) -- smallest and largest allowed values.
+ * If low > high, the value increases as the slider's thumb moves left.
+ *
+ * lowLabel, highLabel (strings) -- labels shown at the left and right
+ * ends of the slider.
+ *
+ * integral (boolean) -- whether to use whole numbers instead of floats;
+ */
+
+package org.jwz.xscreensaver;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.Resources;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.util.Log;
+
+public class SliderPreference extends Preference {
+
+ protected float low, high;
+ protected String low_label, high_label;
+ protected boolean integral;
+ protected float mValue;
+ protected int seekbar_ticks;
+
+ public SliderPreference(Context context, AttributeSet attrs) {
+ this (context, attrs, 0);
+ }
+
+ public SliderPreference (Context context, AttributeSet attrs, int defStyle) {
+ super (context, attrs, defStyle);
+
+ Resources res = context.getResources();
+
+ // Parse these from the "" tag
+ low = Float.parseFloat (attrs.getAttributeValue (null, "low"));
+ high = Float.parseFloat (attrs.getAttributeValue (null, "high"));
+ integral = attrs.getAttributeBooleanValue (null, "integral", false);
+ low_label = res.getString(
+ attrs.getAttributeResourceValue (null, "lowLabel", 0));
+ high_label = res.getString(
+ attrs.getAttributeResourceValue (null, "highLabel", 0));
+
+ seekbar_ticks = (integral
+ ? (int) Math.floor (Math.abs (high - low))
+ : 100000);
+
+ setWidgetLayoutResource (R.layout.slider_preference);
+ }
+
+
+ @Override
+ protected void onSetInitialValue (boolean restore, Object def) {
+ if (restore) {
+ mValue = getPersistedFloat (low);
+ } else {
+ mValue = (Float) def;
+ persistFloat (mValue);
+ }
+ //Log.d("xscreensaver", String.format("SLIDER INIT %s: %f",
+ // low_label, mValue));
+ }
+
+ @Override
+ protected Object onGetDefaultValue(TypedArray a, int index) {
+ return a.getFloat (index, low);
+ }
+
+
+ public float getValue() {
+ return mValue;
+ }
+
+ public void setValue (float value) {
+
+ if (low < high) {
+ value = Math.max (low, Math.min (high, value));
+ } else {
+ value = Math.max (high, Math.min (low, value));
+ }
+
+ if (integral)
+ value = Math.round (value);
+
+ if (value != mValue) {
+ //Log.d("xscreensaver", String.format("SLIDER %s: %f", low_label, value));
+ persistFloat (value);
+ mValue = value;
+ notifyChanged();
+ }
+ }
+
+
+ @Override
+ protected View onCreateView (ViewGroup parent) {
+ View view = super.onCreateView(parent);
+
+ TextView low_view = (TextView)
+ view.findViewById (R.id.slider_preference_low);
+ low_view.setText (low_label);
+
+ TextView high_view = (TextView)
+ view.findViewById (R.id.slider_preference_high);
+ high_view.setText (high_label);
+
+ SeekBar seekbar = (SeekBar)
+ view.findViewById (R.id.slider_preference_seekbar);
+ seekbar.setMax (seekbar_ticks);
+
+ float ratio = (mValue - low) / (high - low);
+ int seek_value = (int) (ratio * (float) seekbar_ticks);
+
+ seekbar.setProgress (seek_value);
+
+ final SliderPreference slider = this;
+
+ seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onProgressChanged (SeekBar seekBar, int progress,
+ boolean fromUser) {
+ if (fromUser) {
+ float ratio = (float) progress / (float) seekbar_ticks;
+ float value = low + (ratio * (high - low));
+ slider.setValue (value);
+ callChangeListener (progress);
+ }
+ }
+ });
+
+ return view;
+ }
+}
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/TTFAnalyzer.java b/android/xscreensaver/src/org/jwz/xscreensaver/TTFAnalyzer.java
new file mode 100644
index 0000000..3d01345
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/TTFAnalyzer.java
@@ -0,0 +1,153 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+
+ * Copyright (C) 2011 George Yunaev @ Ulduzsoft
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+
+ http://www.ulduzsoft.com/2012/01/enumerating-the-fonts-on-android-platform/
+ */
+
+package org.jwz.xscreensaver;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashMap;
+
+// The class which loads the TTF file, parses it and returns the TTF font name
+class TTFAnalyzer
+{
+ // This function parses the TTF file and returns the font name specified in the file
+ public String getTtfFontName( String fontFilename )
+ {
+ try
+ {
+ // Parses the TTF file format.
+ // See http://developer.apple.com/fonts/ttrefman/rm06/Chap6.html
+ m_file = new RandomAccessFile( fontFilename, "r" );
+
+ // Read the version first
+ int version = readDword();
+
+ // The version must be either 'true' (0x74727565) or 0x00010000 or 'OTTO' (0x4f54544f) for CFF style fonts.
+ if ( version != 0x74727565 && version != 0x00010000 && version != 0x4f54544f)
+ return null;
+
+ // The TTF file consist of several sections called "tables", and we need to know how many of them are there.
+ int numTables = readWord();
+
+ // Skip the rest in the header
+ readWord(); // skip searchRange
+ readWord(); // skip entrySelector
+ readWord(); // skip rangeShift
+
+ // Now we can read the tables
+ for ( int i = 0; i < numTables; i++ )
+ {
+ // Read the table entry
+ int tag = readDword();
+ readDword(); // skip checksum
+ int offset = readDword();
+ int length = readDword();
+
+ // Now here' the trick. 'name' field actually contains the textual string name.
+ // So the 'name' string in characters equals to 0x6E616D65
+ if ( tag == 0x6E616D65 )
+ {
+ // Here's the name section. Read it completely into the allocated buffer
+ byte[] table = new byte[ length ];
+
+ m_file.seek( offset );
+ read( table );
+
+ // This is also a table. See http://developer.apple.com/fonts/ttrefman/rm06/Chap6name.html
+ // According to Table 36, the total number of table records is stored in the second word, at the offset 2.
+ // Getting the count and string offset - remembering it's big endian.
+ int count = getWord( table, 2 );
+ int string_offset = getWord( table, 4 );
+
+ // Record starts from offset 6
+ for ( int record = 0; record < count; record++ )
+ {
+ // Table 37 tells us that each record is 6 words -> 12 bytes, and that the nameID is 4th word so its offset is 6.
+ // We also need to account for the first 6 bytes of the header above (Table 36), so...
+ int nameid_offset = record * 12 + 6;
+ int platformID = getWord( table, nameid_offset );
+ int nameid_value = getWord( table, nameid_offset + 6 );
+
+ // Table 42 lists the valid name Identifiers. We're interested in 4 but not in Unicode encoding (for simplicity).
+ // The encoding is stored as PlatformID and we're interested in Mac encoding
+ if ( nameid_value == 4 && platformID == 1 )
+ {
+ // We need the string offset and length, which are the word 6 and 5 respectively
+ int name_length = getWord( table, nameid_offset + 8 );
+ int name_offset = getWord( table, nameid_offset + 10 );
+
+ // The real name string offset is calculated by adding the string_offset
+ name_offset = name_offset + string_offset;
+
+ // Make sure it is inside the array
+ if ( name_offset >= 0 && name_offset + name_length < table.length )
+ return new String( table, name_offset, name_length );
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+ catch (FileNotFoundException e)
+ {
+ // Permissions?
+ return null;
+ }
+ catch (IOException e)
+ {
+ // Most likely a corrupted font file
+ return null;
+ }
+ }
+
+ // Font file; must be seekable
+ private RandomAccessFile m_file = null;
+
+ // Helper I/O functions
+ private int readByte() throws IOException
+ {
+ return m_file.read() & 0xFF;
+ }
+
+ private int readWord() throws IOException
+ {
+ int b1 = readByte();
+ int b2 = readByte();
+
+ return b1 << 8 | b2;
+ }
+
+ private int readDword() throws IOException
+ {
+ int b1 = readByte();
+ int b2 = readByte();
+ int b3 = readByte();
+ int b4 = readByte();
+
+ return b1 << 24 | b2 << 16 | b3 << 8 | b4;
+ }
+
+ private void read( byte [] array ) throws IOException
+ {
+ if ( m_file.read( array ) != array.length )
+ throw new IOException();
+ }
+
+ // Helper
+ private int getWord( byte [] array, int offset )
+ {
+ int b1 = array[ offset ] & 0xFF;
+ int b2 = array[ offset + 1 ] & 0xFF;
+
+ return b1 << 8 | b2;
+ }
+}
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/TVActivity.java b/android/xscreensaver/src/org/jwz/xscreensaver/TVActivity.java
new file mode 100644
index 0000000..0015c9d
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/TVActivity.java
@@ -0,0 +1,50 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * xscreensaver, Copyright (c) 2017 Jamie Zawinski
+ * and Dennis Sheil
+ *
+ * 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.
+ *
+ * This is the XScreenSaver "application" that just brings up the
+ * Daydream preferences for Android TV.
+ */
+
+package org.jwz.xscreensaver;
+
+import android.app.Activity;
+import android.app.WallpaperManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.View;
+import android.provider.Settings;
+
+public class TVActivity extends Activity
+ implements View.OnClickListener {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_tv_xscreensaver);
+ findViewById(R.id.apply_daydream).setOnClickListener(this);
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+
+ case R.id.apply_daydream:
+ String action;
+ Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
+ startActivityForResult(intent, 0);
+ break;
+ }
+ }
+}
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/Wallpaper.java b/android/xscreensaver/src/org/jwz/xscreensaver/Wallpaper.java
new file mode 100644
index 0000000..93896f2
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/Wallpaper.java
@@ -0,0 +1,128 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * xscreensaver, Copyright (c) 2016-2018 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 Wallpaper.
+ *
+ * Each Wallpaper 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.content.res.Configuration;
+import android.service.wallpaper.WallpaperService;
+import android.view.GestureDetector;
+import android.view.SurfaceHolder;
+import android.util.Log;
+import java.lang.RuntimeException;
+import java.lang.Thread;
+import org.jwz.xscreensaver.jwxyz;
+import android.graphics.PixelFormat;
+import android.view.WindowManager;
+import android.view.Display;
+import android.graphics.Point;
+
+public class Wallpaper extends WallpaperService
+/*implements GestureDetector.OnGestureListener,
+ GestureDetector.OnDoubleTapListener, */ {
+
+ /* TODO: Input! */
+ private Engine engine;
+
+ @Override
+ public Engine onCreateEngine() {
+ // Log.d("xscreensaver", "tid = " + Thread.currentThread().getId());
+ engine = new XScreenSaverGLEngine();
+ return engine;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration config) {
+ super.onConfigurationChanged(config);
+ Log.d("xscreensaver", "wallpaper onConfigurationChanged");
+ /*
+ WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+ int width = size.x;
+ int height = size.y;
+ engine.onSurfaceChanged(engine.getSurfaceHolder(), PixelFormat.RGBA_8888, width, height);
+ */
+
+ }
+
+ class XScreenSaverGLEngine extends Engine {
+
+ private boolean initTried = false;
+ private jwxyz jwxyz_obj;
+
+ @Override
+ public void onSurfaceCreated (SurfaceHolder holder) {
+ super.onSurfaceCreated(holder);
+
+ if (!initTried) {
+ initTried = true;
+ } else {
+ if (jwxyz_obj != null) {
+ jwxyz_obj.close();
+ jwxyz_obj = null;
+ }
+ }
+ }
+
+ @Override
+ public void onVisibilityChanged(final boolean visible) {
+ if (jwxyz_obj != null) {
+ if (visible)
+ jwxyz_obj.start();
+ else
+ jwxyz_obj.pause();
+ }
+ }
+
+ @Override
+ public void onSurfaceChanged (SurfaceHolder holder, int format,
+ int width, int height) {
+
+ super.onSurfaceChanged(holder, format, width, height);
+
+ if (width == 0 || height == 0) {
+ jwxyz_obj.close();
+ jwxyz_obj = null;
+ }
+
+ Log.d ("xscreensaver",
+ String.format("surfaceChanged: %dx%d", width, height));
+
+ if (jwxyz_obj == null) {
+ jwxyz_obj = new jwxyz (jwxyz.saverNameOf(Wallpaper.this),
+ Wallpaper.this, null, width, height,
+ holder.getSurface(), null);
+ } else {
+ jwxyz_obj.resize (width, height);
+ }
+
+ jwxyz_obj.start();
+ }
+
+ @Override
+ public void onSurfaceDestroyed (SurfaceHolder holder) {
+ super.onSurfaceDestroyed (holder);
+
+ if (jwxyz_obj != null) {
+ jwxyz_obj.close();
+ jwxyz_obj = null;
+ }
+ }
+ }
+}
diff --git a/android/xscreensaver/src/org/jwz/xscreensaver/jwxyz.java b/android/xscreensaver/src/org/jwz/xscreensaver/jwxyz.java
new file mode 100644
index 0000000..a22a26d
--- /dev/null
+++ b/android/xscreensaver/src/org/jwz/xscreensaver/jwxyz.java
@@ -0,0 +1,1115 @@
+/* -*- Mode: java; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * xscreensaver, Copyright (c) 2016-2018 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.
+ *
+ * This class is how the C implementation of jwxyz calls back into Java
+ * to do things that OpenGL does not have access to without Java-based APIs.
+ * It is the Java companion to jwxyz-android.c and screenhack-android.c.
+ */
+
+package org.jwz.xscreensaver;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.ArrayList;
+import java.util.Random;
+import android.app.AlertDialog;
+import android.view.KeyEvent;
+import android.content.SharedPreferences;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.res.AssetManager;
+import android.graphics.Typeface;
+import android.graphics.Rect;
+import android.graphics.Paint;
+import android.graphics.Paint.FontMetrics;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.net.Uri;
+import android.view.GestureDetector;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.io.File;
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.lang.InterruptedException;
+import java.lang.Runnable;
+import java.lang.Thread;
+import java.util.TimerTask;
+import android.database.Cursor;
+import android.provider.MediaStore;
+import android.provider.MediaStore.MediaColumns;
+import android.media.ExifInterface;
+import org.jwz.xscreensaver.TTFAnalyzer;
+import android.util.Log;
+import android.view.Surface;
+import android.Manifest;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.os.Build;
+import android.content.pm.PackageManager;
+
+public class jwxyz
+ implements GestureDetector.OnGestureListener,
+ GestureDetector.OnDoubleTapListener {
+
+ private class PrefListener
+ implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ @Override
+ public void onSharedPreferenceChanged (SharedPreferences sharedPreferences, String key)
+ {
+ if (key.startsWith(hack + "_")) {
+ if (render != null) {
+ boolean was_animating;
+ synchronized (render) {
+ was_animating = animating_p;
+ }
+ close();
+ if (was_animating)
+ start();
+ }
+ }
+ }
+ };
+
+ private static class SurfaceLost extends Exception {
+ SurfaceLost () {
+ super("surface lost");
+ }
+
+ SurfaceLost (String detailMessage) {
+ super(detailMessage);
+ }
+ }
+
+ public final static int STYLE_BOLD = 1;
+ public final static int STYLE_ITALIC = 2;
+ public final static int STYLE_MONOSPACE = 4;
+
+ public final static int FONT_FAMILY = 0;
+ public final static int FONT_FACE = 1;
+ public final static int FONT_RANDOM = 2;
+
+ public final static int MY_REQ_READ_EXTERNAL_STORAGE = 271828;
+
+ private long nativeRunningHackPtr;
+
+ private String hack;
+ private Context app;
+ private Bitmap screenshot;
+
+ SharedPreferences prefs;
+ SharedPreferences.OnSharedPreferenceChangeListener pref_listener;
+ Hashtable defaults = new Hashtable();
+
+
+ // Maps font names to either: String (system font) or Typeface (bundled).
+ private Hashtable all_fonts =
+ new Hashtable();
+
+ int width, height;
+ Surface surface;
+ boolean animating_p;
+
+ // Doubles as the mutex controlling width/height/animating_p.
+ private Thread render;
+
+ private Runnable on_quit;
+ boolean button_down_p;
+
+ // These are defined in jwxyz-android.c:
+ //
+ private native long nativeInit (String hack,
+ Hashtable defaults,
+ int w, int h, Surface window)
+ throws SurfaceLost;
+ private native void nativeResize (int w, int h, double rot);
+ private native long nativeRender ();
+ private native void nativeDone ();
+ public native void sendButtonEvent (int x, int y, boolean down);
+ public native void sendMotionEvent (int x, int y);
+ public native void sendKeyEvent (boolean down_p, int code, int mods);
+
+ private void LOG (String fmt, Object... args) {
+ Log.d ("xscreensaver", hack + ": " + String.format (fmt, args));
+ }
+
+ static public String saverNameOf (Object obj) {
+ // Extract the saver name from e.g. "gen.Daydream$BouncingCow"
+ String name = obj.getClass().getSimpleName();
+ int index = name.lastIndexOf('$');
+ if (index != -1) {
+ index++;
+ name = name.substring (index, name.length() - index);
+ }
+ return name.toLowerCase();
+ }
+
+ // Constructor
+ public jwxyz (String hack, Context app, Bitmap screenshot, int w, int h,
+ Surface surface, Runnable on_quit) {
+
+ this.hack = hack;
+ this.app = app;
+ this.screenshot = screenshot;
+ this.on_quit = on_quit;
+ this.width = w;
+ this.height = h;
+ this.surface = surface;
+
+ // nativeInit populates 'defaults' with the default values for keys
+ // that are not overridden by SharedPreferences.
+
+ prefs = app.getSharedPreferences (hack, 0);
+
+ // Keep a strong reference to pref_listener, because
+ // registerOnSharedPreferenceChangeListener only uses a weak reference.
+ pref_listener = new PrefListener();
+ prefs.registerOnSharedPreferenceChangeListener (pref_listener);
+
+ scanSystemFonts();
+ }
+
+ protected void finalize() {
+ if (render != null) {
+ LOG ("jwxyz finalized without close. This might be OK.");
+ close();
+ }
+ }
+
+
+ public String getStringResource (String name) {
+
+ name = hack + "_" + name;
+
+ if (prefs.contains(name)) {
+
+ // SharedPreferences is very picky that you request the exact type that
+ // was stored: if it is a float and you ask for a string, you get an
+ // exception instead of the float converted to a string.
+
+ String s = null;
+ try { return prefs.getString (name, "");
+ } catch (Exception e) { }
+
+ try { return Float.toString (prefs.getFloat (name, 0));
+ } catch (Exception e) { }
+
+ try { return Long.toString (prefs.getLong (name, 0));
+ } catch (Exception e) { }
+
+ try { return Integer.toString (prefs.getInt (name, 0));
+ } catch (Exception e) { }
+
+ try { return (prefs.getBoolean (name, false) ? "true" : "false");
+ } catch (Exception e) { }
+ }
+
+ // If we got to here, it's not in there, so return the default.
+ return defaults.get (name);
+ }
+
+
+ private String mungeFontName (String name) {
+ // Roboto-ThinItalic => RobotoThin
+ // AndroidCock Regular => AndroidClock
+ String tails[] = { "Bold", "Italic", "Oblique", "Regular" };
+ for (String tail : tails) {
+ String pres[] = { " ", "-", "_", "" };
+ for (String pre : pres) {
+ int i = name.indexOf(pre + tail);
+ if (i > 0) name = name.substring (0, i);
+ }
+ }
+ return name;
+ }
+
+
+ private void scanSystemFonts() {
+
+ // First parse the system font directories for the global fonts.
+
+ String[] fontdirs = { "/system/fonts", "/system/font", "/data/fonts" };
+ TTFAnalyzer analyzer = new TTFAnalyzer();
+ for (String fontdir : fontdirs) {
+ File dir = new File(fontdir);
+ if (!dir.exists())
+ continue;
+ File[] files = dir.listFiles();
+ if (files == null)
+ continue;
+
+ for (File file : files) {
+ String name = analyzer.getTtfFontName (file.getAbsolutePath());
+ if (name == null) {
+ // LOG ("unparsable system font: %s", file);
+ } else {
+ name = mungeFontName (name);
+ if (! all_fonts.contains (name)) {
+ // LOG ("system font \"%s\" %s", name, file);
+ all_fonts.put (name, name);
+ }
+ }
+ }
+ }
+
+ // Now parse our assets, for our bundled fonts.
+
+ AssetManager am = app.getAssets();
+ String dir = "fonts";
+ String[] files = null;
+ try { files = am.list(dir); }
+ catch (Exception e) { LOG("listing assets: %s", e.toString()); }
+
+ for (String fn : files) {
+ String fn2 = dir + "/" + fn;
+ Typeface t = Typeface.createFromAsset (am, fn2);
+
+ File tmpfile = null;
+ try {
+ tmpfile = new File(app.getCacheDir(), fn);
+ if (tmpfile.createNewFile() == false) {
+ tmpfile.delete();
+ tmpfile.createNewFile();
+ }
+
+ InputStream in = am.open (fn2);
+ FileOutputStream out = new FileOutputStream (tmpfile);
+ byte[] buffer = new byte[1024 * 512];
+ while (in.read(buffer, 0, 1024 * 512) != -1) {
+ out.write(buffer);
+ }
+ out.close();
+ in.close();
+
+ String name = analyzer.getTtfFontName (tmpfile.getAbsolutePath());
+ tmpfile.delete();
+
+ name = mungeFontName (name);
+ all_fonts.put (name, t);
+ // LOG ("asset font \"%s\" %s", name, fn);
+ } catch (Exception e) {
+ if (tmpfile != null) tmpfile.delete();
+ LOG ("error: %s", e.toString());
+ }
+ }
+ }
+
+
+ // Parses family names from X Logical Font Descriptions, including a few
+ // standard X font names that aren't handled by try_xlfd_font().
+ // Returns [ String name, Typeface ]
+ private Object[] parseXLFD (int mask, int traits,
+ String name, int name_type) {
+ boolean fixed = false;
+ boolean serif = false;
+
+ int style_jwxyz = mask & traits;
+
+ if (name_type != FONT_RANDOM) {
+ if ((style_jwxyz & STYLE_BOLD) != 0 ||
+ name.equals("fixed") ||
+ name.equals("courier") ||
+ name.equals("console") ||
+ name.equals("lucidatypewriter") ||
+ name.equals("monospace")) {
+ fixed = true;
+ } else if (name.equals("times") ||
+ name.equals("georgia") ||
+ name.equals("serif")) {
+ serif = true;
+ } else if (name.equals("serif-monospace")) {
+ fixed = true;
+ serif = true;
+ }
+ } else {
+ Random r = new Random();
+ serif = r.nextBoolean(); // Not much to randomize here...
+ fixed = (r.nextInt(8) == 0);
+ }
+
+ name = (fixed
+ ? (serif ? "serif-monospace" : "monospace")
+ : (serif ? "serif" : "sans-serif"));
+
+ int style_android = 0;
+ if ((style_jwxyz & STYLE_BOLD) != 0)
+ style_android |= Typeface.BOLD;
+ if ((style_jwxyz & STYLE_ITALIC) != 0)
+ style_android |= Typeface.ITALIC;
+
+ return new Object[] { name, Typeface.create(name, style_android) };
+ }
+
+
+ // Parses "Native Font Name One 12, Native Font Name Two 14".
+ // Returns [ String name, Typeface ]
+ private Object[] parseNativeFont (String name) {
+ Object font2 = all_fonts.get (name);
+ if (font2 instanceof String)
+ font2 = Typeface.create (name, Typeface.NORMAL);
+ return new Object[] { name, (Typeface)font2 };
+ }
+
+
+ // Returns [ Paint paint, String family_name, Float ascent, Float descent ]
+ public Object[] loadFont(int mask, int traits, String name, int name_type,
+ float size) {
+ Object pair[];
+
+ if (name_type != FONT_RANDOM && name.equals("")) return null;
+
+ if (name_type == FONT_FACE) {
+ pair = parseNativeFont (name);
+ } else {
+ pair = parseXLFD (mask, traits, name, name_type);
+ }
+
+ String name2 = (String) pair[0];
+ Typeface font = (Typeface) pair[1];
+
+ size *= 2;
+
+ String suffix = (font.isBold() && font.isItalic() ? " bold italic" :
+ font.isBold() ? " bold" :
+ font.isItalic() ? " italic" :
+ "");
+ Paint paint = new Paint();
+ paint.setTypeface (font);
+ paint.setTextSize (size);
+ paint.setColor (Color.argb (0xFF, 0xFF, 0xFF, 0xFF));
+
+ LOG ("load font \"%s\" = \"%s %.1f\"", name, name2 + suffix, size);
+
+ FontMetrics fm = paint.getFontMetrics();
+ return new Object[] { paint, name2, -fm.ascent, fm.descent };
+ }
+
+
+ /* Returns a byte[] array containing XCharStruct with an optional
+ bitmap appended to it.
+ lbearing, rbearing, width, ascent, descent: 2 bytes each.
+ Followed by a WxH pixmap, 32 bits per pixel.
+ */
+ public ByteBuffer renderText (Paint paint, String text, boolean render_p,
+ boolean antialias_p) {
+
+ if (paint == null) {
+ LOG ("no font");
+ return null;
+ }
+
+ /* Font metric terminology, as used by X11:
+
+ "lbearing" is the distance from the logical origin to the leftmost
+ pixel. If a character's ink extends to the left of the origin, it is
+ negative.
+
+ "rbearing" is the distance from the logical origin to the rightmost
+ pixel.
+
+ "descent" is the distance from the logical origin to the bottommost
+ pixel. For characters with descenders, it is positive. For
+ superscripts, it is negative.
+
+ "ascent" is the distance from the logical origin to the topmost pixel.
+ It is the number of pixels above the baseline.
+
+ "width" is the distance from the logical origin to the position where
+ the logical origin of the next character should be placed.
+
+ If "rbearing" is greater than "width", then this character overlaps the
+ following character. If smaller, then there is trailing blank space.
+
+ The bbox coordinates returned by getTextBounds grow down and right:
+ for a character with ink both above and below the baseline, top is
+ negative and bottom is positive.
+ */
+ paint.setAntiAlias (antialias_p);
+ FontMetrics fm = paint.getFontMetrics();
+ Rect bbox = new Rect();
+ paint.getTextBounds (text, 0, text.length(), bbox);
+
+ /* The bbox returned by getTextBounds measures from the logical origin
+ with right and down being positive. This means most characters have
+ a negative top, and characters with descenders have a positive bottom.
+ */
+ int lbearing = bbox.left;
+ int rbearing = bbox.right;
+ int ascent = -bbox.top;
+ int descent = bbox.bottom;
+ int width = (int) paint.measureText (text);
+
+ int w = rbearing - lbearing;
+ int h = ascent + descent;
+ int size = 5 * 2 + (render_p ? w * h * 4 : 0);
+
+ ByteBuffer bits = ByteBuffer.allocateDirect (size);
+
+ bits.put ((byte) ((lbearing >> 8) & 0xFF));
+ bits.put ((byte) ( lbearing & 0xFF));
+ bits.put ((byte) ((rbearing >> 8) & 0xFF));
+ bits.put ((byte) ( rbearing & 0xFF));
+ bits.put ((byte) ((width >> 8) & 0xFF));
+ bits.put ((byte) ( width & 0xFF));
+ bits.put ((byte) ((ascent >> 8) & 0xFF));
+ bits.put ((byte) ( ascent & 0xFF));
+ bits.put ((byte) ((descent >> 8) & 0xFF));
+ bits.put ((byte) ( descent & 0xFF));
+
+ if (render_p && w > 0 && h > 0) {
+ Bitmap bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas (bitmap);
+ canvas.drawText (text, -lbearing, ascent, paint);
+ bitmap.copyPixelsToBuffer (bits);
+ bitmap.recycle();
+ }
+
+ return bits;
+ }
+
+
+ /* Returns the contents of the URL.
+ Loads the URL in a background thread: if the URL has not yet loaded,
+ this will return null. Once the URL has completely loaded, the full
+ contents will be returned. Calling this again after that starts the
+ URL loading again.
+ */
+ private String loading_url = null;
+ private ByteBuffer loaded_url_body = null;
+
+ public synchronized ByteBuffer loadURL (String url) {
+
+ if (loaded_url_body != null) { // Thread finished
+
+ // LOG ("textclient finished %s", loading_url);
+
+ ByteBuffer bb = loaded_url_body;
+ loading_url = null;
+ loaded_url_body = null;
+ return bb;
+
+ } else if (loading_url != null) { // Waiting on thread
+ // LOG ("textclient waiting...");
+ return null;
+
+ } else { // Launch thread
+
+ loading_url = url;
+ LOG ("textclient launching %s...", url);
+
+ new Thread (new Runnable() {
+ public void run() {
+ int size0 = 10240;
+ int size = size0;
+ int count = 0;
+ ByteBuffer body = ByteBuffer.allocateDirect (size);
+
+ try {
+ URL u = new URL (loading_url);
+ // LOG ("textclient thread loading: %s", u.toString());
+ InputStream s = u.openStream();
+ byte buf[] = new byte[10240];
+ while (true) {
+ int n = s.read (buf);
+ if (n == -1) break;
+ // LOG ("textclient thread read %d", n);
+ if (count + n + 1 >= size) {
+ int size2 = (int) (size * 1.2 + size0);
+ // LOG ("textclient thread expand %d -> %d", size, size2);
+ ByteBuffer body2 = ByteBuffer.allocateDirect (size2);
+ body.rewind();
+ body2.put (body);
+ body2.position (count);
+ body = body2;
+ size = size2;
+ }
+ body.put (buf, 0, n);
+ count += n;
+ }
+ } catch (Exception e) {
+ LOG ("load URL error: %s", e.toString());
+ body.clear();
+ body.put (e.toString().getBytes());
+ body.put ((byte) 0);
+ }
+
+ // LOG ("textclient thread finished %s (%d)", loading_url, size);
+ loaded_url_body = body;
+ }
+ }).start();
+
+ return null;
+ }
+ }
+
+
+ // Returns [ Bitmap bitmap, String name ]
+ private Object[] convertBitmap (String name, Bitmap bitmap,
+ int target_width, int target_height,
+ ExifInterface exif, boolean rotate_p) {
+ if (bitmap == null) return null;
+
+ {
+
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+ Matrix matrix = new Matrix();
+
+ LOG ("read image %s: %d x %d", name, width, height);
+
+ // First rotate the image as per EXIF.
+
+ if (exif != null) {
+ int deg = 0;
+ switch (exif.getAttributeInt (ExifInterface.TAG_ORIENTATION,
+ ExifInterface.ORIENTATION_NORMAL)) {
+ case ExifInterface.ORIENTATION_ROTATE_90: deg = 90; break;
+ case ExifInterface.ORIENTATION_ROTATE_180: deg = 180; break;
+ case ExifInterface.ORIENTATION_ROTATE_270: deg = 270; break;
+ }
+ if (deg != 0) {
+ LOG ("%s: EXIF rotate %d", name, deg);
+ matrix.preRotate (deg);
+ if (deg == 90 || deg == 270) {
+ int temp = width;
+ width = height;
+ height = temp;
+ }
+ }
+ }
+
+ // If the caller requested that we rotate the image to best fit the
+ // screen, rotate it again.
+
+ if (rotate_p &&
+ (width > height) != (target_width > target_height)) {
+ LOG ("%s: rotated to fit screen", name);
+ matrix.preRotate (90);
+
+ int temp = width;
+ width = height;
+ height = temp;
+ }
+
+ // Resize the image to be not larger than the screen, potentially
+ // copying it for the third time.
+ // Actually, always scale it, scaling up if necessary.
+
+// if (width > target_width || height > target_height)
+ {
+ float r1 = target_width / (float) width;
+ float r2 = target_height / (float) height;
+ float r = (r1 > r2 ? r2 : r1);
+ LOG ("%s: resize %.1f: %d x %d => %d x %d", name,
+ r, width, height, (int) (width * r), (int) (height * r));
+ matrix.preScale (r, r);
+ }
+
+ bitmap = Bitmap.createBitmap (bitmap, 0, 0,
+ bitmap.getWidth(), bitmap.getHeight(),
+ matrix, true);
+
+ if (bitmap.getConfig() != Bitmap.Config.ARGB_8888)
+ bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false);
+
+ return new Object[] { bitmap, name };
+
+ }
+ }
+
+
+ boolean havePermission(String permission) {
+
+ if (Build.VERSION.SDK_INT < 16) {
+ return true;
+ }
+
+ if (permissionGranted(permission)) {
+ return true;
+ }
+
+ return false;
+ }
+
+
+ private boolean permissionGranted(String permission) {
+ boolean check = ContextCompat.checkSelfPermission(app, permission) ==
+ PackageManager.PERMISSION_GRANTED;
+ return check;
+ }
+
+ public Object[] checkThenLoadRandomImage (int target_width, int target_height,
+ boolean rotate_p) {
+ // RES introduced in API 16
+ String permission = Manifest.permission.READ_EXTERNAL_STORAGE;
+
+ if (havePermission(permission)) {
+ return loadRandomImage(target_width,target_height,rotate_p);
+ } else {
+ return null;
+ }
+ }
+
+ public Object[] loadRandomImage (int target_width, int target_height,
+ boolean rotate_p) {
+
+ int min_size = 480;
+ int max_size = 0x7FFF;
+
+ ArrayList imgs = new ArrayList();
+
+ ContentResolver cr = app.getContentResolver();
+ String[] cols = { MediaColumns.DATA,
+ MediaColumns.MIME_TYPE,
+ MediaColumns.WIDTH,
+ MediaColumns.HEIGHT };
+ Uri uris[] = {
+ android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI,
+ android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI };
+
+ for (int i = 0; i < uris.length; i++) {
+ Cursor cursor = cr.query (uris[i], cols, null, null, null);
+ if (cursor == null)
+ continue;
+ int j = 0;
+ int path_col = cursor.getColumnIndexOrThrow (cols[j++]);
+ int type_col = cursor.getColumnIndexOrThrow (cols[j++]);
+ int width_col = cursor.getColumnIndexOrThrow (cols[j++]);
+ int height_col = cursor.getColumnIndexOrThrow (cols[j++]);
+ while (cursor.moveToNext()) {
+ String path = cursor.getString(path_col);
+ String type = cursor.getString(type_col);
+ if (path != null && type != null && type.startsWith("image/")) {
+ String wc = cursor.getString(width_col);
+ String hc = cursor.getString(height_col);
+ if (wc != null && hc != null) {
+ int w = Integer.parseInt (wc);
+ int h = Integer.parseInt (hc);
+ if (w > min_size && h > min_size &&
+ w < max_size && h < max_size) {
+ imgs.add (path);
+ }
+ }
+ }
+ }
+ cursor.close();
+ }
+
+ String which = null;
+
+ int count = imgs.size();
+ if (count == 0) {
+ LOG ("no images");
+ return null;
+ }
+
+ int i = new Random().nextInt (count);
+ which = imgs.get (i);
+ LOG ("picked image %d of %d: %s", i, count, which);
+
+ Uri uri = Uri.fromFile (new File (which));
+ String name = uri.getLastPathSegment();
+ Bitmap bitmap = null;
+ ExifInterface exif = null;
+
+ try {
+ try {
+ bitmap = MediaStore.Images.Media.getBitmap (cr, uri);
+ } catch (Exception e) {
+ LOG ("image %s unloadable: %s", which, e.toString());
+ return null;
+ }
+
+ try {
+ exif = new ExifInterface (uri.getPath()); // If it fails, who cares
+ } catch (Exception e) {
+ }
+
+ return convertBitmap (name, bitmap, target_width, target_height,
+ exif, rotate_p);
+ } catch (java.lang.OutOfMemoryError e) {
+ LOG ("image %s got OutOfMemoryError: %s", which, e.toString());
+ return null;
+ }
+ }
+
+
+ public Object[] getScreenshot (int target_width, int target_height,
+ boolean rotate_p) {
+ return convertBitmap ("Screenshot", screenshot,
+ target_width, target_height,
+ null, rotate_p);
+ }
+
+
+ public Bitmap decodePNG (byte[] data) {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ return BitmapFactory.decodeByteArray (data, 0, data.length, opts);
+ }
+
+
+ // Sadly duplicated from jwxyz.h (and thence X.h and keysymdef.h)
+ //
+ private static final int ShiftMask = (1<<0);
+ private static final int LockMask = (1<<1);
+ private static final int ControlMask = (1<<2);
+ private static final int Mod1Mask = (1<<3);
+ private static final int Mod2Mask = (1<<4);
+ private static final int Mod3Mask = (1<<5);
+ private static final int Mod4Mask = (1<<6);
+ private static final int Mod5Mask = (1<<7);
+ private static final int Button1Mask = (1<<8);
+ private static final int Button2Mask = (1<<9);
+ private static final int Button3Mask = (1<<10);
+ private static final int Button4Mask = (1<<11);
+ private static final int Button5Mask = (1<<12);
+
+ private static final int XK_Shift_L = 0xFFE1;
+ private static final int XK_Shift_R = 0xFFE2;
+ private static final int XK_Control_L = 0xFFE3;
+ private static final int XK_Control_R = 0xFFE4;
+ private static final int XK_Caps_Lock = 0xFFE5;
+ private static final int XK_Shift_Lock = 0xFFE6;
+ private static final int XK_Meta_L = 0xFFE7;
+ private static final int XK_Meta_R = 0xFFE8;
+ private static final int XK_Alt_L = 0xFFE9;
+ private static final int XK_Alt_R = 0xFFEA;
+ private static final int XK_Super_L = 0xFFEB;
+ private static final int XK_Super_R = 0xFFEC;
+ private static final int XK_Hyper_L = 0xFFED;
+ private static final int XK_Hyper_R = 0xFFEE;
+
+ private static final int XK_Home = 0xFF50;
+ private static final int XK_Left = 0xFF51;
+ private static final int XK_Up = 0xFF52;
+ private static final int XK_Right = 0xFF53;
+ private static final int XK_Down = 0xFF54;
+ private static final int XK_Prior = 0xFF55;
+ private static final int XK_Page_Up = 0xFF55;
+ private static final int XK_Next = 0xFF56;
+ private static final int XK_Page_Down = 0xFF56;
+ private static final int XK_End = 0xFF57;
+ private static final int XK_Begin = 0xFF58;
+
+ private static final int XK_F1 = 0xFFBE;
+ private static final int XK_F2 = 0xFFBF;
+ private static final int XK_F3 = 0xFFC0;
+ private static final int XK_F4 = 0xFFC1;
+ private static final int XK_F5 = 0xFFC2;
+ private static final int XK_F6 = 0xFFC3;
+ private static final int XK_F7 = 0xFFC4;
+ private static final int XK_F8 = 0xFFC5;
+ private static final int XK_F9 = 0xFFC6;
+ private static final int XK_F10 = 0xFFC7;
+ private static final int XK_F11 = 0xFFC8;
+ private static final int XK_F12 = 0xFFC9;
+
+ public void sendKeyEvent (KeyEvent event) {
+ int uc = event.getUnicodeChar();
+ int jcode = event.getKeyCode();
+ int jmods = event.getModifiers();
+ int xcode = 0;
+ int xmods = 0;
+
+ switch (jcode) {
+ case KeyEvent.KEYCODE_SHIFT_LEFT: xcode = XK_Shift_L; break;
+ case KeyEvent.KEYCODE_SHIFT_RIGHT: xcode = XK_Shift_R; break;
+ case KeyEvent.KEYCODE_CTRL_LEFT: xcode = XK_Control_L; break;
+ case KeyEvent.KEYCODE_CTRL_RIGHT: xcode = XK_Control_R; break;
+ case KeyEvent.KEYCODE_CAPS_LOCK: xcode = XK_Caps_Lock; break;
+ case KeyEvent.KEYCODE_META_LEFT: xcode = XK_Meta_L; break;
+ case KeyEvent.KEYCODE_META_RIGHT: xcode = XK_Meta_R; break;
+ case KeyEvent.KEYCODE_ALT_LEFT: xcode = XK_Alt_L; break;
+ case KeyEvent.KEYCODE_ALT_RIGHT: xcode = XK_Alt_R; break;
+
+ case KeyEvent.KEYCODE_HOME: xcode = XK_Home; break;
+ case KeyEvent.KEYCODE_DPAD_LEFT: xcode = XK_Left; break;
+ case KeyEvent.KEYCODE_DPAD_UP: xcode = XK_Up; break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT: xcode = XK_Right; break;
+ case KeyEvent.KEYCODE_DPAD_DOWN: xcode = XK_Down; break;
+ //case KeyEvent.KEYCODE_NAVIGATE_PREVIOUS: xcode = XK_Prior; break;
+ case KeyEvent.KEYCODE_PAGE_UP: xcode = XK_Page_Up; break;
+ //case KeyEvent.KEYCODE_NAVIGATE_NEXT: xcode = XK_Next; break;
+ case KeyEvent.KEYCODE_PAGE_DOWN: xcode = XK_Page_Down; break;
+ case KeyEvent.KEYCODE_MOVE_END: xcode = XK_End; break;
+ case KeyEvent.KEYCODE_MOVE_HOME: xcode = XK_Begin; break;
+
+ case KeyEvent.KEYCODE_F1: xcode = XK_F1; break;
+ case KeyEvent.KEYCODE_F2: xcode = XK_F2; break;
+ case KeyEvent.KEYCODE_F3: xcode = XK_F3; break;
+ case KeyEvent.KEYCODE_F4: xcode = XK_F4; break;
+ case KeyEvent.KEYCODE_F5: xcode = XK_F5; break;
+ case KeyEvent.KEYCODE_F6: xcode = XK_F6; break;
+ case KeyEvent.KEYCODE_F7: xcode = XK_F7; break;
+ case KeyEvent.KEYCODE_F8: xcode = XK_F8; break;
+ case KeyEvent.KEYCODE_F9: xcode = XK_F9; break;
+ case KeyEvent.KEYCODE_F10: xcode = XK_F10; break;
+ case KeyEvent.KEYCODE_F11: xcode = XK_F11; break;
+ case KeyEvent.KEYCODE_F12: xcode = XK_F12; break;
+ default: xcode = uc; break;
+ }
+
+ if (0 != (jmods & KeyEvent.META_SHIFT_ON)) xmods |= ShiftMask;
+ if (0 != (jmods & KeyEvent.META_CAPS_LOCK_ON)) xmods |= LockMask;
+ if (0 != (jmods & KeyEvent.META_CTRL_MASK)) xmods |= ControlMask;
+ if (0 != (jmods & KeyEvent.META_ALT_MASK)) xmods |= Mod1Mask;
+ if (0 != (jmods & KeyEvent.META_META_ON)) xmods |= Mod1Mask;
+ if (0 != (jmods & KeyEvent.META_SYM_ON)) xmods |= Mod2Mask;
+ if (0 != (jmods & KeyEvent.META_FUNCTION_ON)) xmods |= Mod3Mask;
+
+ /* If you touch and release Shift, you get no events.
+ If you type Shift-A, you get Shift down, A down, A up, Shift up.
+ So let's just ignore all lone modifier key events.
+ */
+ if (xcode >= XK_Shift_L && xcode <= XK_Hyper_R)
+ return;
+
+ boolean down_p = event.getAction() == KeyEvent.ACTION_DOWN;
+ sendKeyEvent (down_p, xcode, xmods);
+ }
+
+ void start () {
+ if (render == null) {
+ animating_p = true;
+ render = new Thread(new Runnable() {
+ @Override
+ public void run()
+ {
+ int currentWidth, currentHeight;
+ synchronized (render) {
+ while (true) {
+ while (!animating_p || width == 0 || height == 0) {
+ try {
+ render.wait();
+ } catch(InterruptedException exc) {
+ return;
+ }
+ }
+
+ try {
+ nativeInit (hack, defaults, width, height, surface);
+ currentWidth = width;
+ currentHeight= height;
+ break;
+ } catch (SurfaceLost exc) {
+ width = 0;
+ height = 0;
+ }
+ }
+ }
+
+ main_loop:
+ while (true) {
+ synchronized (render) {
+ assert width != 0;
+ assert height != 0;
+ while (!animating_p) {
+ try {
+ render.wait();
+ } catch(InterruptedException exc) {
+ break main_loop;
+ }
+ }
+
+ if (currentWidth != width || currentHeight != height) {
+ currentWidth = width;
+ currentHeight = height;
+ nativeResize (width, height, 0);
+ }
+ }
+
+ long delay = nativeRender();
+
+ synchronized (render) {
+ if (delay != 0) {
+ try {
+ render.wait(delay / 1000, (int)(delay % 1000) * 1000);
+ } catch (InterruptedException exc) {
+ break main_loop;
+ }
+ } else {
+ if (Thread.interrupted ()) {
+ break main_loop;
+ }
+ }
+ }
+ }
+
+ assert nativeRunningHackPtr != 0;
+ nativeDone ();
+ }
+ });
+
+ render.start();
+ } else {
+ synchronized(render) {
+ animating_p = true;
+ render.notify();
+ }
+ }
+ }
+
+ void pause () {
+ if (render == null)
+ return;
+ synchronized (render) {
+ animating_p = false;
+ render.notify();
+ }
+ }
+
+ void close () {
+ if (render == null)
+ return;
+ synchronized (render) {
+ animating_p = false;
+ render.interrupt();
+ }
+ try {
+ render.join();
+ } catch (InterruptedException exc) {
+ }
+ render = null;
+ }
+
+ void resize (int w, int h) {
+ assert w != 0;
+ assert h != 0;
+ if (render != null) {
+ synchronized (render) {
+ width = w;
+ height = h;
+ render.notify();
+ }
+ } else {
+ width = w;
+ height = h;
+ }
+ }
+
+
+ /* 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) {
+ if (on_quit != null)
+ on_quit.run();
+ return true;
+ }
+
+ @Override
+ public boolean onDoubleTap (MotionEvent event) {
+ 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));
+ sendButtonEvent (x, y, true);
+ 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));
+ sendButtonEvent (x, y, true);
+ }
+ }
+
+ @Override
+ public boolean onScroll (MotionEvent e1, MotionEvent e2,
+ float distanceX, float distanceY) {
+ // LOG ("onScroll: %d", button_down_p ? 1 : 0);
+ if (button_down_p)
+ 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));
+ 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;
+ }
+
+
+ static {
+ System.loadLibrary ("xscreensaver");
+
+/*
+ Thread.setDefaultUncaughtExceptionHandler(
+ new Thread.UncaughtExceptionHandler() {
+ Thread.UncaughtExceptionHandler old_handler =
+ Thread.currentThread().getUncaughtExceptionHandler();
+
+ @Override
+ public void uncaughtException (Thread thread, Throwable ex) {
+ String err = ex.toString();
+ Log.d ("xscreensaver", "Caught exception: " + err);
+ old_handler.uncaughtException (thread, ex);
+ }
+ });
+*/
+ }
+}
diff --git a/android/xscreensaver/xscreensaver.iml b/android/xscreensaver/xscreensaver.iml
new file mode 100644
index 0000000..dd9566c
--- /dev/null
+++ b/android/xscreensaver/xscreensaver.iml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
--
cgit v1.2.3-55-g7522