1 /*
2  * Copyright (C) 2009 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.example.android.livecubes.cube2;
18 
19 import android.content.SharedPreferences;
20 import android.graphics.Canvas;
21 import android.graphics.Paint;
22 import android.graphics.Rect;
23 import android.os.Handler;
24 import android.os.SystemClock;
25 import android.service.wallpaper.WallpaperService;
26 import android.view.MotionEvent;
27 import android.view.SurfaceHolder;
28 
29 /*
30  * This animated wallpaper draws a rotating wireframe shape. It is similar to
31  * example #1, but has a choice of 2 shapes, which are user selectable and
32  * defined in resources instead of in code.
33  */
34 
35 public class CubeWallpaper2 extends WallpaperService {
36 
37     public static final String SHARED_PREFS_NAME="cube2settings";
38 
39     static class ThreeDPoint {
40         float x;
41         float y;
42         float z;
43     }
44 
45     static class ThreeDLine {
46         int startPoint;
47         int endPoint;
48     }
49 
50     @Override
onCreate()51     public void onCreate() {
52         super.onCreate();
53     }
54 
55     @Override
onDestroy()56     public void onDestroy() {
57         super.onDestroy();
58     }
59 
60     @Override
onCreateEngine()61     public Engine onCreateEngine() {
62         return new CubeEngine();
63     }
64 
65     class CubeEngine extends Engine
66         implements SharedPreferences.OnSharedPreferenceChangeListener {
67 
68         private final Handler mHandler = new Handler();
69 
70         ThreeDPoint [] mOriginalPoints;
71         ThreeDPoint [] mRotatedPoints;
72         ThreeDLine [] mLines;
73         private final Paint mPaint = new Paint();
74         private float mOffset;
75         private float mTouchX = -1;
76         private float mTouchY = -1;
77         private long mStartTime;
78         private float mCenterX;
79         private float mCenterY;
80 
81         private final Runnable mDrawCube = new Runnable() {
82             public void run() {
83                 drawFrame();
84             }
85         };
86         private boolean mVisible;
87         private SharedPreferences mPrefs;
88 
CubeEngine()89         CubeEngine() {
90             // Create a Paint to draw the lines for our cube
91             final Paint paint = mPaint;
92             paint.setColor(0xffffffff);
93             paint.setAntiAlias(true);
94             paint.setStrokeWidth(2);
95             paint.setStrokeCap(Paint.Cap.ROUND);
96             paint.setStyle(Paint.Style.STROKE);
97 
98             mStartTime = SystemClock.elapsedRealtime();
99 
100             mPrefs = CubeWallpaper2.this.getSharedPreferences(SHARED_PREFS_NAME, 0);
101             mPrefs.registerOnSharedPreferenceChangeListener(this);
102             onSharedPreferenceChanged(mPrefs, null);
103         }
104 
onSharedPreferenceChanged(SharedPreferences prefs, String key)105         public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
106 
107             String shape = prefs.getString("cube2_shape", "cube");
108 
109             // read the 3D model from the resource
110             readModel(shape);
111         }
112 
readModel(String prefix)113         private void readModel(String prefix) {
114             // Read the model definition in from a resource.
115 
116             // get the resource identifiers for the arrays for the selected shape
117             int pid = getResources().getIdentifier(prefix + "points", "array", getPackageName());
118             int lid = getResources().getIdentifier(prefix + "lines", "array", getPackageName());
119 
120             String [] p = getResources().getStringArray(pid);
121             int numpoints = p.length;
122             mOriginalPoints = new ThreeDPoint[numpoints];
123             mRotatedPoints = new ThreeDPoint[numpoints];
124 
125             for (int i = 0; i < numpoints; i++) {
126                 mOriginalPoints[i] = new ThreeDPoint();
127                 mRotatedPoints[i] = new ThreeDPoint();
128                 String [] coord = p[i].split(" ");
129                 mOriginalPoints[i].x = Float.valueOf(coord[0]);
130                 mOriginalPoints[i].y = Float.valueOf(coord[1]);
131                 mOriginalPoints[i].z = Float.valueOf(coord[2]);
132             }
133 
134             String [] l = getResources().getStringArray(lid);
135             int numlines = l.length;
136             mLines = new ThreeDLine[numlines];
137 
138             for (int i = 0; i < numlines; i++) {
139                 mLines[i] = new ThreeDLine();
140                 String [] idx = l[i].split(" ");
141                 mLines[i].startPoint = Integer.parseInt(idx[0]);
142                 mLines[i].endPoint = Integer.parseInt(idx[1]);
143             }
144         }
145 
146         @Override
onCreate(SurfaceHolder surfaceHolder)147         public void onCreate(SurfaceHolder surfaceHolder) {
148             super.onCreate(surfaceHolder);
149             setTouchEventsEnabled(true);
150         }
151 
152         @Override
onDestroy()153         public void onDestroy() {
154             super.onDestroy();
155             mHandler.removeCallbacks(mDrawCube);
156         }
157 
158         @Override
onVisibilityChanged(boolean visible)159         public void onVisibilityChanged(boolean visible) {
160             mVisible = visible;
161             if (visible) {
162                 drawFrame();
163             } else {
164                 mHandler.removeCallbacks(mDrawCube);
165             }
166         }
167 
168         @Override
onSurfaceChanged(SurfaceHolder holder, int format, int width, int height)169         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
170             super.onSurfaceChanged(holder, format, width, height);
171             // store the center of the surface, so we can draw the cube in the right spot
172             mCenterX = width/2.0f;
173             mCenterY = height/2.0f;
174             drawFrame();
175         }
176 
177         @Override
onSurfaceCreated(SurfaceHolder holder)178         public void onSurfaceCreated(SurfaceHolder holder) {
179             super.onSurfaceCreated(holder);
180         }
181 
182         @Override
onSurfaceDestroyed(SurfaceHolder holder)183         public void onSurfaceDestroyed(SurfaceHolder holder) {
184             super.onSurfaceDestroyed(holder);
185             mVisible = false;
186             mHandler.removeCallbacks(mDrawCube);
187         }
188 
189         @Override
onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels)190         public void onOffsetsChanged(float xOffset, float yOffset,
191                 float xStep, float yStep, int xPixels, int yPixels) {
192             mOffset = xOffset;
193             drawFrame();
194         }
195 
196         /*
197          * Store the position of the touch event so we can use it for drawing later
198          */
199         @Override
onTouchEvent(MotionEvent event)200         public void onTouchEvent(MotionEvent event) {
201             if (event.getAction() == MotionEvent.ACTION_MOVE) {
202                 mTouchX = event.getX();
203                 mTouchY = event.getY();
204             } else {
205                 mTouchX = -1;
206                 mTouchY = -1;
207             }
208             super.onTouchEvent(event);
209         }
210 
211         /*
212          * Draw one frame of the animation. This method gets called repeatedly
213          * by posting a delayed Runnable. You can do any drawing you want in
214          * here. This example draws a wireframe cube.
215          */
drawFrame()216         void drawFrame() {
217             final SurfaceHolder holder = getSurfaceHolder();
218             final Rect frame = holder.getSurfaceFrame();
219             final int width = frame.width();
220             final int height = frame.height();
221 
222             Canvas c = null;
223             try {
224                 c = holder.lockCanvas();
225                 if (c != null) {
226                     // draw something
227                     drawCube(c);
228                     drawTouchPoint(c);
229                 }
230             } finally {
231                 if (c != null) holder.unlockCanvasAndPost(c);
232             }
233 
234             mHandler.removeCallbacks(mDrawCube);
235             if (mVisible) {
236                 mHandler.postDelayed(mDrawCube, 1000 / 25);
237             }
238         }
239 
drawCube(Canvas c)240         void drawCube(Canvas c) {
241             c.save();
242             c.translate(mCenterX, mCenterY);
243             c.drawColor(0xff000000);
244 
245             long now = SystemClock.elapsedRealtime();
246             float xrot = ((float)(now - mStartTime)) / 1000;
247             float yrot = (0.5f - mOffset) * 2.0f;
248             rotateAndProjectPoints(xrot, yrot);
249             drawLines(c);
250             c.restore();
251         }
252 
rotateAndProjectPoints(float xrot, float yrot)253         void rotateAndProjectPoints(float xrot, float yrot) {
254             int n = mOriginalPoints.length;
255             for (int i = 0; i < n; i++) {
256                 // rotation around X-axis
257                 ThreeDPoint p = mOriginalPoints[i];
258                 float x = p.x;
259                 float y = p.y;
260                 float z = p.z;
261                 float newy = (float)(Math.sin(xrot) * z + Math.cos(xrot) * y);
262                 float newz = (float)(Math.cos(xrot) * z - Math.sin(xrot) * y);
263 
264                 // rotation around Y-axis
265                 float newx = (float)(Math.sin(yrot) * newz + Math.cos(yrot) * x);
266                 newz = (float)(Math.cos(yrot) * newz - Math.sin(yrot) * x);
267 
268                 // 3D-to-2D projection
269                 float screenX = newx / (4 - newz / 400);
270                 float screenY = newy / (4 - newz / 400);
271 
272                 mRotatedPoints[i].x = screenX;
273                 mRotatedPoints[i].y = screenY;
274                 mRotatedPoints[i].z = 0;
275             }
276         }
277 
drawLines(Canvas c)278         void drawLines(Canvas c) {
279             int n = mLines.length;
280             for (int i = 0; i < n; i++) {
281                 ThreeDLine l = mLines[i];
282                 ThreeDPoint start = mRotatedPoints[l.startPoint];
283                 ThreeDPoint end = mRotatedPoints[l.endPoint];
284                 c.drawLine(start.x, start.y, end.x, end.y, mPaint);
285             }
286         }
287 
drawTouchPoint(Canvas c)288         void drawTouchPoint(Canvas c) {
289             if (mTouchX >=0 && mTouchY >= 0) {
290                 c.drawCircle(mTouchX, mTouchY, 80, mPaint);
291             }
292         }
293     }
294 }
295