OSDN Git Service

resolved conflicts for merge of acb5f383 to jb-dev-plus-aosp
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / ImageWallpaper.java
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.android.systemui;
18
19 import android.app.ActivityManager;
20 import android.app.WallpaperManager;
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.graphics.Bitmap;
25 import android.graphics.Canvas;
26 import android.graphics.Rect;
27 import android.graphics.Region.Op;
28 import android.opengl.GLUtils;
29 import android.os.SystemProperties;
30 import android.renderscript.Matrix4f;
31 import android.service.wallpaper.WallpaperService;
32 import android.util.Log;
33 import android.view.Display;
34 import android.view.MotionEvent;
35 import android.view.SurfaceHolder;
36 import android.view.WindowManager;
37
38 import javax.microedition.khronos.egl.EGL10;
39 import javax.microedition.khronos.egl.EGLConfig;
40 import javax.microedition.khronos.egl.EGLContext;
41 import javax.microedition.khronos.egl.EGLDisplay;
42 import javax.microedition.khronos.egl.EGLSurface;
43 import javax.microedition.khronos.opengles.GL;
44 import java.io.IOException;
45 import java.nio.ByteBuffer;
46 import java.nio.ByteOrder;
47 import java.nio.FloatBuffer;
48
49 import static android.opengl.GLES20.*;
50 import static javax.microedition.khronos.egl.EGL10.*;
51
52 /**
53  * Default built-in wallpaper that simply shows a static image.
54  */
55 @SuppressWarnings({"UnusedDeclaration"})
56 public class ImageWallpaper extends WallpaperService {
57     private static final String TAG = "ImageWallpaper";
58     private static final String GL_LOG_TAG = "ImageWallpaperGL";
59     private static final boolean DEBUG = false;
60     private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
61
62     static final boolean FIXED_SIZED_SURFACE = true;
63     static final boolean USE_OPENGL = true;
64
65     WallpaperManager mWallpaperManager;
66
67     boolean mIsHwAccelerated;
68
69     @Override
70     public void onCreate() {
71         super.onCreate();
72         mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
73
74         //noinspection PointlessBooleanExpression,ConstantConditions
75         if (FIXED_SIZED_SURFACE && USE_OPENGL) {
76             if (!isEmulator()) {
77                 WindowManager windowManager =
78                         (WindowManager) getSystemService(Context.WINDOW_SERVICE);
79                 Display display = windowManager.getDefaultDisplay();
80                 mIsHwAccelerated = ActivityManager.isHighEndGfx(display);
81             }
82         }
83     }
84
85     private static boolean isEmulator() {
86         return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
87     }
88
89     public Engine onCreateEngine() {
90         return new DrawableEngine();
91     }
92
93     class DrawableEngine extends Engine {
94         static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
95         static final int EGL_OPENGL_ES2_BIT = 4;
96
97         private final Object mLock = new Object[0];
98
99         // TODO: Not currently used, keeping around until we know we don't need it
100         @SuppressWarnings({"UnusedDeclaration"})
101         private WallpaperObserver mReceiver;
102
103         Bitmap mBackground;
104         int mBackgroundWidth = -1, mBackgroundHeight = -1;
105         float mXOffset;
106         float mYOffset;
107
108         boolean mVisible = true;
109         boolean mRedrawNeeded;
110         boolean mOffsetsChanged;
111         int mLastXTranslation;
112         int mLastYTranslation;
113
114         private EGL10 mEgl;
115         private EGLDisplay mEglDisplay;
116         private EGLConfig mEglConfig;
117         private EGLContext mEglContext;
118         private EGLSurface mEglSurface;
119         private GL mGL;
120
121         private static final String sSimpleVS =
122                 "attribute vec4 position;\n" +
123                 "attribute vec2 texCoords;\n" +
124                 "varying vec2 outTexCoords;\n" +
125                 "uniform mat4 projection;\n" +
126                 "\nvoid main(void) {\n" +
127                 "    outTexCoords = texCoords;\n" +
128                 "    gl_Position = projection * position;\n" +
129                 "}\n\n";
130         private static final String sSimpleFS =
131                 "precision mediump float;\n\n" +
132                 "varying vec2 outTexCoords;\n" +
133                 "uniform sampler2D texture;\n" +
134                 "\nvoid main(void) {\n" +
135                 "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
136                 "}\n\n";
137     
138         private static final int FLOAT_SIZE_BYTES = 4;
139         private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
140         private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
141         private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
142
143         class WallpaperObserver extends BroadcastReceiver {
144             public void onReceive(Context context, Intent intent) {
145                 if (DEBUG) {
146                     Log.d(TAG, "onReceive");
147                 }
148
149                 synchronized (mLock) {
150                     mBackgroundWidth = mBackgroundHeight = -1;
151                     mBackground = null;
152                     mRedrawNeeded = true;
153                     drawFrameLocked();
154                 }
155             }
156         }
157
158         public DrawableEngine() {
159             super();
160             setFixedSizeAllowed(true);
161         }
162
163         @Override
164         public void onCreate(SurfaceHolder surfaceHolder) {
165             if (DEBUG) {
166                 Log.d(TAG, "onCreate");
167             }
168
169             super.onCreate(surfaceHolder);
170             
171             // TODO: Don't need this currently because the wallpaper service
172             // will restart the image wallpaper whenever the image changes.
173             //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
174             //mReceiver = new WallpaperObserver();
175             //registerReceiver(mReceiver, filter, null, mHandler);
176
177             updateSurfaceSize(surfaceHolder);
178
179             setOffsetNotificationsEnabled(false);
180         }
181
182         @Override
183         public void onDestroy() {
184             super.onDestroy();
185             if (mReceiver != null) {
186                 unregisterReceiver(mReceiver);
187             }
188         }
189
190         @Override
191         public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
192             super.onDesiredSizeChanged(desiredWidth, desiredHeight);
193             SurfaceHolder surfaceHolder = getSurfaceHolder();
194             if (surfaceHolder != null) {
195                 updateSurfaceSize(surfaceHolder);
196             }
197         }
198
199         void updateSurfaceSize(SurfaceHolder surfaceHolder) {
200             if (FIXED_SIZED_SURFACE) {
201                 // Used a fixed size surface, because we are special.  We can do
202                 // this because we know the current design of window animations doesn't
203                 // cause this to break.
204                 surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
205             } else {
206                 surfaceHolder.setSizeFromLayout();
207             }
208         }
209
210         @Override
211         public void onVisibilityChanged(boolean visible) {
212             if (DEBUG) {
213                 Log.d(TAG, "onVisibilityChanged: visible=" + visible);
214             }
215
216             synchronized (mLock) {
217                 if (mVisible != visible) {
218                     if (DEBUG) {
219                         Log.d(TAG, "Visibility changed to visible=" + visible);
220                     }
221                     mVisible = visible;
222                     drawFrameLocked();
223                 }
224             }
225         }
226
227         @Override
228         public void onTouchEvent(MotionEvent event) {
229             super.onTouchEvent(event);
230         }
231
232         @Override
233         public void onOffsetsChanged(float xOffset, float yOffset,
234                 float xOffsetStep, float yOffsetStep,
235                 int xPixels, int yPixels) {
236             if (DEBUG) {
237                 Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
238                         + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
239                         + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
240             }
241
242             synchronized (mLock) {
243                 if (mXOffset != xOffset || mYOffset != yOffset) {
244                     if (DEBUG) {
245                         Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
246                     }
247                     mXOffset = xOffset;
248                     mYOffset = yOffset;
249                     mOffsetsChanged = true;
250                 }
251                 drawFrameLocked();
252             }
253         }
254
255         @Override
256         public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
257             if (DEBUG) {
258                 Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
259             }
260
261             super.onSurfaceChanged(holder, format, width, height);
262
263             synchronized (mLock) {
264                 mRedrawNeeded = true;
265                 drawFrameLocked();
266             }
267         }
268
269         void drawFrameLocked() {
270             if (!mVisible) {
271                 if (DEBUG) {
272                     Log.d(TAG, "Suppressed drawFrame since wallpaper is not visible.");
273                 }
274                 return;
275             }
276             if (!mRedrawNeeded && !mOffsetsChanged) {
277                 if (DEBUG) {
278                     Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
279                             + "and offsets have not changed.");
280                 }
281                 return;
282             }
283
284             if (mBackgroundWidth < 0 || mBackgroundHeight < 0) {
285                 // If we don't yet know the size of the wallpaper bitmap,
286                 // we need to get it now.
287                 updateWallpaperLocked();
288             }
289
290             if (mBackground == null) {
291                 // If we somehow got to this point after we have last flushed
292                 // the wallpaper, well we really need it to draw again.  So
293                 // seems like we need to reload it.  Ouch.
294                 updateWallpaperLocked();
295             }
296
297             SurfaceHolder sh = getSurfaceHolder();
298             final Rect frame = sh.getSurfaceFrame();
299             final int dw = frame.width();
300             final int dh = frame.height();
301             final int availw = dw - mBackgroundWidth;
302             final int availh = dh - mBackgroundHeight;
303             int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
304             int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
305
306             mOffsetsChanged = false;
307             if (!mRedrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
308                 if (DEBUG) {
309                     Log.d(TAG, "Suppressed drawFrame since the image has not "
310                             + "actually moved an integral number of pixels.");
311                 }
312                 return;
313             }
314             mRedrawNeeded = false;
315             mLastXTranslation = xPixels;
316             mLastYTranslation = yPixels;
317
318             if (mIsHwAccelerated) {
319                 if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
320                     drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
321                 }
322             } else {
323                 drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
324             }
325
326             if (FIXED_SIZED_SURFACE) {
327                 // If the surface is fixed-size, we should only need to
328                 // draw it once and then we'll let the window manager
329                 // position it appropriately.  As such, we no longer needed
330                 // the loaded bitmap.  Yay!
331                 mBackground = null;
332                 mWallpaperManager.forgetLoadedWallpaper();
333             }
334         }
335
336         void updateWallpaperLocked() {
337             Throwable exception = null;
338             try {
339                 mBackground = mWallpaperManager.getBitmap();
340             } catch (RuntimeException e) {
341                 exception = e;
342             } catch (OutOfMemoryError e) {
343                 exception = e;
344             }
345
346             if (exception != null) {
347                 mBackground = null;
348                 // Note that if we do fail at this, and the default wallpaper can't
349                 // be loaded, we will go into a cycle.  Don't do a build where the
350                 // default wallpaper can't be loaded.
351                 Log.w(TAG, "Unable to load wallpaper!", exception);
352                 try {
353                     mWallpaperManager.clear();
354                 } catch (IOException ex) {
355                     // now we're really screwed.
356                     Log.w(TAG, "Unable reset to default wallpaper!", ex);
357                 }
358             }
359
360             mBackgroundWidth = mBackground != null ? mBackground.getWidth() : 0;
361             mBackgroundHeight = mBackground != null ? mBackground.getHeight() : 0;
362         }
363
364         private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
365             Canvas c = sh.lockCanvas();
366             if (c != null) {
367                 try {
368                     if (DEBUG) {
369                         Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
370                     }
371
372                     c.translate(x, y);
373                     if (w < 0 || h < 0) {
374                         c.save(Canvas.CLIP_SAVE_FLAG);
375                         c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE);
376                         c.drawColor(0xff000000);
377                         c.restore();
378                     }
379                     if (mBackground != null) {
380                         c.drawBitmap(mBackground, 0, 0, null);
381                     }
382                 } finally {
383                     sh.unlockCanvasAndPost(c);
384                 }
385             }
386         }
387
388         private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
389             if (!initGL(sh)) return false;
390
391             final float right = left + mBackgroundWidth;
392             final float bottom = top + mBackgroundHeight;
393
394             final Rect frame = sh.getSurfaceFrame();
395
396             final Matrix4f ortho = new Matrix4f();
397             ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
398
399             final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
400
401             final int texture = loadTexture(mBackground);
402             final int program = buildProgram(sSimpleVS, sSimpleFS);
403     
404             final int attribPosition = glGetAttribLocation(program, "position");
405             final int attribTexCoords = glGetAttribLocation(program, "texCoords");
406             final int uniformTexture = glGetUniformLocation(program, "texture");
407             final int uniformProjection = glGetUniformLocation(program, "projection");
408
409             checkGlError();
410
411             glViewport(0, 0, frame.width(), frame.height());
412             glBindTexture(GL_TEXTURE_2D, texture);
413
414             glUseProgram(program);
415             glEnableVertexAttribArray(attribPosition);
416             glEnableVertexAttribArray(attribTexCoords);
417             glUniform1i(uniformTexture, 0);
418             glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
419
420             checkGlError();
421
422             if (w < 0 || h < 0) {
423                 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
424                 glClear(GL_COLOR_BUFFER_BIT);
425             }
426     
427             // drawQuad
428             triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
429             glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
430                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
431
432             triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
433             glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
434                     TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
435
436             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
437     
438             if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
439                 throw new RuntimeException("Cannot swap buffers");
440             }
441             checkEglError();
442     
443             finishGL();
444
445             return true;
446         }
447
448         private FloatBuffer createMesh(int left, int top, float right, float bottom) {
449             final float[] verticesData = {
450                     // X, Y, Z, U, V
451                      left,  bottom, 0.0f, 0.0f, 1.0f,
452                      right, bottom, 0.0f, 1.0f, 1.0f,
453                      left,  top,    0.0f, 0.0f, 0.0f,
454                      right, top,    0.0f, 1.0f, 0.0f,
455             };
456
457             final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
458             final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
459                     ByteOrder.nativeOrder()).asFloatBuffer();
460             triangleVertices.put(verticesData).position(0);
461             return triangleVertices;
462         }
463
464         private int loadTexture(Bitmap bitmap) {
465             int[] textures = new int[1];
466     
467             glActiveTexture(GL_TEXTURE0);
468             glGenTextures(1, textures, 0);
469             checkGlError();
470     
471             int texture = textures[0];
472             glBindTexture(GL_TEXTURE_2D, texture);
473             checkGlError();
474             
475             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
476             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
477     
478             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
479             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
480     
481             GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
482             checkGlError();
483
484             bitmap.recycle();
485     
486             return texture;
487         }
488         
489         private int buildProgram(String vertex, String fragment) {
490             int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
491             if (vertexShader == 0) return 0;
492     
493             int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
494             if (fragmentShader == 0) return 0;
495     
496             int program = glCreateProgram();
497             glAttachShader(program, vertexShader);
498             checkGlError();
499     
500             glAttachShader(program, fragmentShader);
501             checkGlError();
502     
503             glLinkProgram(program);
504             checkGlError();
505     
506             int[] status = new int[1];
507             glGetProgramiv(program, GL_LINK_STATUS, status, 0);
508             if (status[0] != GL_TRUE) {
509                 String error = glGetProgramInfoLog(program);
510                 Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
511                 glDeleteShader(vertexShader);
512                 glDeleteShader(fragmentShader);
513                 glDeleteProgram(program);
514                 return 0;
515             }
516     
517             return program;
518         }
519         
520         private int buildShader(String source, int type) {
521             int shader = glCreateShader(type);
522     
523             glShaderSource(shader, source);
524             checkGlError();
525     
526             glCompileShader(shader);
527             checkGlError();
528     
529             int[] status = new int[1];
530             glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
531             if (status[0] != GL_TRUE) {
532                 String error = glGetShaderInfoLog(shader);
533                 Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
534                 glDeleteShader(shader);
535                 return 0;
536             }
537             
538             return shader;
539         }
540     
541         private void checkEglError() {
542             int error = mEgl.eglGetError();
543             if (error != EGL_SUCCESS) {
544                 Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
545             }
546         }
547     
548         private void checkGlError() {
549             int error = glGetError();
550             if (error != GL_NO_ERROR) {
551                 Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
552             }
553         }
554     
555         private void finishGL() {
556             mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
557             mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
558             mEgl.eglDestroyContext(mEglDisplay, mEglContext);
559         }
560         
561         private boolean initGL(SurfaceHolder surfaceHolder) {
562             mEgl = (EGL10) EGLContext.getEGL();
563     
564             mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
565             if (mEglDisplay == EGL_NO_DISPLAY) {
566                 throw new RuntimeException("eglGetDisplay failed " +
567                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
568             }
569             
570             int[] version = new int[2];
571             if (!mEgl.eglInitialize(mEglDisplay, version)) {
572                 throw new RuntimeException("eglInitialize failed " +
573                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
574             }
575     
576             mEglConfig = chooseEglConfig();
577             if (mEglConfig == null) {
578                 throw new RuntimeException("eglConfig not initialized");
579             }
580             
581             mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
582     
583             mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
584     
585             if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
586                 int error = mEgl.eglGetError();
587                 if (error == EGL_BAD_NATIVE_WINDOW) {
588                     Log.e(GL_LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
589                     return false;
590                 }
591                 throw new RuntimeException("createWindowSurface failed " +
592                         GLUtils.getEGLErrorString(error));
593             }
594     
595             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
596                 throw new RuntimeException("eglMakeCurrent failed " +
597                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
598             }
599     
600             mGL = mEglContext.getGL();
601
602             return true;
603         }
604         
605     
606         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
607             int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
608             return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);            
609         }
610     
611         private EGLConfig chooseEglConfig() {
612             int[] configsCount = new int[1];
613             EGLConfig[] configs = new EGLConfig[1];
614             int[] configSpec = getConfig();
615             if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
616                 throw new IllegalArgumentException("eglChooseConfig failed " +
617                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
618             } else if (configsCount[0] > 0) {
619                 return configs[0];
620             }
621             return null;
622         }
623
624         private int[] getConfig() {
625             return new int[] {
626                     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
627                     EGL_RED_SIZE, 8,
628                     EGL_GREEN_SIZE, 8,
629                     EGL_BLUE_SIZE, 8,
630                     EGL_ALPHA_SIZE, 0,
631                     EGL_DEPTH_SIZE, 0,
632                     EGL_STENCIL_SIZE, 0,
633                     EGL_NONE
634             };
635         }
636     }
637 }