summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChris Wren <cwren@android.com>2012-08-31 12:46:30 -0400
committerChris Wren <cwren@android.com>2012-09-04 12:59:28 -0400
commitc91cbab0936ec64c8078aa8deeb1cfbe0e15cbfa (patch)
tree4abc9159672334cdf283808537e59b26a36560b2
parentf61019ceb816fed9e5035c3d9b8451f6e4ee1da9 (diff)
downloadandroid_packages_screensavers_PhotoTable-c91cbab0936ec64c8078aa8deeb1cfbe0e15cbfa.tar.gz
android_packages_screensavers_PhotoTable-c91cbab0936ec64c8078aa8deeb1cfbe0e15cbfa.tar.bz2
android_packages_screensavers_PhotoTable-c91cbab0936ec64c8078aa8deeb1cfbe0e15cbfa.zip
add a simple photo flipper dream.
Change-Id: I33a5dc491445ba6203e744099187df2760486be6
-rw-r--r--AndroidManifest.xml14
-rw-r--r--res/layout/carousel.xml36
-rw-r--r--res/layout/photo.xml4
-rw-r--r--res/mipmap-hdpi/flip.pngbin0 -> 9222 bytes
-rw-r--r--res/mipmap-mdpi/flip.pngbin0 -> 5924 bytes
-rw-r--r--res/mipmap-xhdpi/flip.pngbin0 -> 11919 bytes
-rw-r--r--res/values/config.xml3
-rw-r--r--res/values/strings.xml5
-rw-r--r--src/com/android/dreams/phototable/FlipperDream.java38
-rw-r--r--src/com/android/dreams/phototable/PhotoCarousel.java206
-rw-r--r--src/com/android/dreams/phototable/PhotoTable.java2
11 files changed, 302 insertions, 6 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d675626..4511728 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -12,7 +12,19 @@
android:hardwareAccelerated="true"
<service android:name="PhotoTable"
android:exported="true"
- android:label="@string/screensaver_name">
+ android:icon="@mipmap/icon"
+ android:label="@string/table_screensaver_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.DREAM" />
+ </intent-filter>
+ </service>
+ <service android:name="FlipperDream"
+ android:exported="true"
+ android:icon="@mipmap/flip"
+ android:label="@string/flipper_screensaver_name">
+ android:icon="@mipmap/flip"
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
diff --git a/res/layout/carousel.xml b/res/layout/carousel.xml
new file mode 100644
index 0000000..bf7ce12
--- /dev/null
+++ b/res/layout/carousel.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.dreams.phototable.PhotoCarousel
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/carousel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/front"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerInside"
+ />
+ <ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/back"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="centerInside"
+ android:alpha="0"
+ android:rotationY="180"
+ />
+</com.android.dreams.phototable.PhotoCarousel>
+
diff --git a/res/layout/photo.xml b/res/layout/photo.xml
index 1850c21..fb3969a 100644
--- a/res/layout/photo.xml
+++ b/res/layout/photo.xml
@@ -15,8 +15,8 @@
-->
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/photo"
- android:layout_width="1024dp"
- android:layout_height="1024dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:scaleType="centerInside"
/>
diff --git a/res/mipmap-hdpi/flip.png b/res/mipmap-hdpi/flip.png
new file mode 100644
index 0000000..6f5f2b4
--- /dev/null
+++ b/res/mipmap-hdpi/flip.png
Binary files differ
diff --git a/res/mipmap-mdpi/flip.png b/res/mipmap-mdpi/flip.png
new file mode 100644
index 0000000..c4ec665
--- /dev/null
+++ b/res/mipmap-mdpi/flip.png
Binary files differ
diff --git a/res/mipmap-xhdpi/flip.png b/res/mipmap-xhdpi/flip.png
new file mode 100644
index 0000000..df97926
--- /dev/null
+++ b/res/mipmap-xhdpi/flip.png
Binary files differ
diff --git a/res/values/config.xml b/res/values/config.xml
index 74e1bfd..2a848ba 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -23,6 +23,9 @@
<!-- Milliseconds to wait before the next immediate drop.-->
<integer name="now_drop">500</integer>
+ <!-- Length of flip animation in milliseconds.-->
+ <integer name="flip_duration">500</integer>
+
<!-- Maximum number of photos to leave on the table.-->
<integer name="table_capacity">10</integer>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 94e2f9c..897f5b2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<resources>
- <string name="app_name">Photo Table Screensaver</string>
- <string name="screensaver_name">Photo Table</string>
+ <string name="app_name">Photo Screensavers</string>
+ <string name="table_screensaver_name">Photo Table</string>
+ <string name="flipper_screensaver_name">Photo Flipper</string>
</resources>
diff --git a/src/com/android/dreams/phototable/FlipperDream.java b/src/com/android/dreams/phototable/FlipperDream.java
new file mode 100644
index 0000000..f7b1cfe
--- /dev/null
+++ b/src/com/android/dreams/phototable/FlipperDream.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dreams.phototable;
+
+import android.service.dreams.Dream;
+
+/**
+ * Example interactive screen saver: single photo with flipping.
+ */
+public class FlipperDream extends Dream {
+ private static final String TAG = "FlipperDream";
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ setInteractive(true);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ lightsOut();
+ setContentView(R.layout.carousel);
+ }
+}
diff --git a/src/com/android/dreams/phototable/PhotoCarousel.java b/src/com/android/dreams/phototable/PhotoCarousel.java
new file mode 100644
index 0000000..62a44f5
--- /dev/null
+++ b/src/com/android/dreams/phototable/PhotoCarousel.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dreams.phototable;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.AsyncTask;
+import android.service.dreams.Dream;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+/**
+ * A FrameLayout that holds two photos, back to back.
+ */
+public class PhotoCarousel extends FrameLayout {
+ private static final String TAG = "PhotoCarousel";
+
+ private final Flipper mFlipper;
+ private final LocalSource mLocalSource;
+ private final StockSource mStockSource;
+ private final GestureDetector mGestureDetector;
+ private final View[] mPanel;
+ private final BitmapFactory.Options mOptions;
+ private final int mFlipDuration;
+ private final int mDropPeriod;
+ private boolean mOnce;
+ private int mLongSide;
+ private int mShortSide;
+
+ class Flipper implements Runnable {
+ @Override
+ public void run() {
+ PhotoCarousel.this.flip(1f);
+ }
+ }
+
+ public PhotoCarousel(Context context, AttributeSet as) {
+ super(context, as);
+ final Resources resources = getResources();
+ mDropPeriod = resources.getInteger(R.integer.drop_period);
+ mFlipDuration = resources.getInteger(R.integer.flip_duration);
+ mOptions = new BitmapFactory.Options();
+ mOptions.inTempStorage = new byte[32768];
+ mLocalSource = new LocalSource(context);
+ mStockSource = new StockSource(context);
+ mPanel = new View[2];
+ mFlipper = new Flipper();
+ mGestureDetector = new GestureDetector(context,
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float vX, float vY) {
+ Log.i(TAG, "fling with " + vX);
+ flip(Math.signum(vX));
+ return true;
+ }
+ });
+ }
+
+ private float lockTo180(float a) {
+ return 180f * (float) Math.floor(a / 180f);
+ }
+
+ private float wrap360(float a) {
+ return a - 360f * (float) Math.floor(a / 360f);
+ }
+
+ private class PhotoLoadTask extends AsyncTask<Void, Void, Bitmap> {
+ private PhotoCarousel mCarousel;
+ private ImageView mDestination;
+
+ public PhotoLoadTask(PhotoCarousel carousel, View destination) {
+ mCarousel = carousel;
+ mDestination = (ImageView) destination;
+ }
+ @Override
+ public Bitmap doInBackground(Void... unused) {
+ Bitmap decodedPhoto = null;
+ decodedPhoto = mCarousel.mLocalSource.next(mCarousel.mOptions,
+ mCarousel.mLongSide, mCarousel.mShortSide);
+ if (decodedPhoto == null) {
+ decodedPhoto = mCarousel.mStockSource.next(mCarousel.mOptions,
+ mCarousel.mLongSide, mCarousel.mShortSide);
+ }
+ return decodedPhoto;
+ }
+
+ @Override
+ public void onPostExecute(Bitmap photo) {
+ if (photo != null) {
+ mDestination.setImageBitmap(photo);
+ mCarousel.requestLayout();
+ }
+ }
+ };
+
+ public void flip(float sgn) {
+ mPanel[0].animate().cancel();
+ mPanel[1].animate().cancel();
+
+ float frontY = mPanel[0].getRotationY();
+ float backY = mPanel[1].getRotationY();
+
+ frontY = wrap360(frontY);
+ backY = wrap360(backY);
+
+ mPanel[0].setRotationY(frontY);
+ mPanel[1].setRotationY(backY);
+
+ frontY = lockTo180(frontY + sgn * 180f);
+ backY = lockTo180(backY + sgn * 180f);
+
+ float frontA = 1f;
+ float backA = 0f;
+ if (frontY == 180f || frontY == -180f) {
+ frontA = 0f;
+ backA = 1f;
+ } else {
+ frontA = 1f;
+ backA = 0f;
+ }
+
+ ViewPropertyAnimator frontAnim = mPanel[0].animate()
+ .rotationY(frontY)
+ .alpha(frontA)
+ .setDuration(mFlipDuration);
+ ViewPropertyAnimator backAnim = mPanel[1].animate()
+ .rotationY(backY)
+ .alpha(backA)
+ .setDuration(mFlipDuration);
+
+ int replaceIdx = 1;
+ ViewPropertyAnimator replaceAnim = backAnim;
+ if (frontA == 0f) {
+ replaceAnim = frontAnim;
+ replaceIdx = 0;
+ }
+
+ final View replaceView = mPanel[replaceIdx];
+ replaceAnim.withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ new PhotoLoadTask(PhotoCarousel.this, replaceView)
+ .execute();
+ }
+ });
+
+ frontAnim.start();
+ backAnim.start();
+
+ scheduleNext(mDropPeriod);
+ }
+
+ @Override
+ public void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ int height = bottom - top;
+ int width = right - left;
+
+ mLongSide = (int) Math.max(width, height);
+ mShortSide = (int) Math.min(width, height);
+
+ if (!mOnce) {
+ mOnce = true;
+
+ mPanel[0] = findViewById(R.id.front);
+ mPanel[1] = findViewById(R.id.back);
+
+ new PhotoLoadTask(this, mPanel[0]).execute();
+ new PhotoLoadTask(this, mPanel[1]).execute();
+
+ scheduleNext(mDropPeriod);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ mGestureDetector.onTouchEvent(event);
+ return true;
+ }
+
+ public void scheduleNext(int delay) {
+ removeCallbacks(mFlipper);
+ postDelayed(mFlipper, delay);
+ }
+}
diff --git a/src/com/android/dreams/phototable/PhotoTable.java b/src/com/android/dreams/phototable/PhotoTable.java
index 47a80d9..49c4b92 100644
--- a/src/com/android/dreams/phototable/PhotoTable.java
+++ b/src/com/android/dreams/phototable/PhotoTable.java
@@ -18,7 +18,7 @@ package com.android.dreams.phototable;
import android.service.dreams.Dream;
/**
- * Example interactive screen saver.
+ * Example interactive screen saver: flick photos onto a table.
*/
public class PhotoTable extends Dream {
private static final String TAG = "PhotoTable";