blob: cdd3d84a96fb22fc75c7107b7e2c2562263c85f9 [file] [log] [blame]
Dianne Hackborn4c62fc02009-08-08 20:40:27 -07001/*
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
Dianne Hackbornba398392011-08-01 16:11:57 -070017package com.android.systemui;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070018
Romain Guy407ec782011-08-24 17:06:58 -070019import android.app.ActivityManager;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070020import android.app.WallpaperManager;
Romain Guy407ec782011-08-24 17:06:58 -070021import android.content.BroadcastReceiver;
Chet Haasec61d70e2012-10-10 15:41:57 -070022import android.content.ComponentCallbacks2;
Romain Guy407ec782011-08-24 17:06:58 -070023import android.content.Context;
24import android.content.Intent;
25import android.graphics.Bitmap;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070026import android.graphics.Canvas;
Dianne Hackborn759a39e2009-08-09 17:20:27 -070027import android.graphics.Rect;
Mathias Agopiane2d034c2009-09-23 21:06:17 -070028import android.graphics.Region.Op;
Romain Guy407ec782011-08-24 17:06:58 -070029import android.opengl.GLUtils;
Romain Guy043a6b12011-09-27 15:37:54 -070030import android.os.SystemProperties;
Romain Guy407ec782011-08-24 17:06:58 -070031import android.renderscript.Matrix4f;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070032import android.service.wallpaper.WallpaperService;
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -080033import android.util.Log;
Dianne Hackborn8df8b2b2009-08-17 15:15:18 -070034import android.view.MotionEvent;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070035import android.view.SurfaceHolder;
Chet Haase2f200812012-10-11 12:49:08 -070036import android.view.WindowManager;
Romain Guy407ec782011-08-24 17:06:58 -070037
38import javax.microedition.khronos.egl.EGL10;
39import javax.microedition.khronos.egl.EGLConfig;
40import javax.microedition.khronos.egl.EGLContext;
41import javax.microedition.khronos.egl.EGLDisplay;
42import javax.microedition.khronos.egl.EGLSurface;
Romain Guy407ec782011-08-24 17:06:58 -070043import java.io.IOException;
44import java.nio.ByteBuffer;
45import java.nio.ByteOrder;
46import java.nio.FloatBuffer;
47
48import static android.opengl.GLES20.*;
49import static javax.microedition.khronos.egl.EGL10.*;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070050
51/**
52 * Default built-in wallpaper that simply shows a static image.
53 */
Romain Guy407ec782011-08-24 17:06:58 -070054@SuppressWarnings({"UnusedDeclaration"})
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070055public class ImageWallpaper extends WallpaperService {
Jeff Brownfa2e5042011-01-23 13:14:23 -080056 private static final String TAG = "ImageWallpaper";
Romain Guy407ec782011-08-24 17:06:58 -070057 private static final String GL_LOG_TAG = "ImageWallpaperGL";
Jeff Brownfa2e5042011-01-23 13:14:23 -080058 private static final boolean DEBUG = false;
Romain Guy043a6b12011-09-27 15:37:54 -070059 private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
Jeff Brownfa2e5042011-01-23 13:14:23 -080060
Dianne Hackbornba398392011-08-01 16:11:57 -070061 static final boolean FIXED_SIZED_SURFACE = true;
Erik Gilling881fb202011-08-25 10:27:52 -070062 static final boolean USE_OPENGL = true;
Dianne Hackbornba398392011-08-01 16:11:57 -070063
Romain Guyef654bd2009-08-11 19:12:17 -070064 WallpaperManager mWallpaperManager;
Romain Guy407ec782011-08-24 17:06:58 -070065
Chet Haasec61d70e2012-10-10 15:41:57 -070066 DrawableEngine mEngine;
67
Romain Guy407ec782011-08-24 17:06:58 -070068 boolean mIsHwAccelerated;
Dianne Hackborn759a39e2009-08-09 17:20:27 -070069
Romain Guyef654bd2009-08-11 19:12:17 -070070 @Override
71 public void onCreate() {
72 super.onCreate();
73 mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
Romain Guy407ec782011-08-24 17:06:58 -070074
75 //noinspection PointlessBooleanExpression,ConstantConditions
76 if (FIXED_SIZED_SURFACE && USE_OPENGL) {
Romain Guy043a6b12011-09-27 15:37:54 -070077 if (!isEmulator()) {
Jeff Brown98365d72012-08-19 20:30:52 -070078 mIsHwAccelerated = ActivityManager.isHighEndGfx();
Romain Guy043a6b12011-09-27 15:37:54 -070079 }
Romain Guy407ec782011-08-24 17:06:58 -070080 }
Romain Guyef654bd2009-08-11 19:12:17 -070081 }
82
Chet Haasec61d70e2012-10-10 15:41:57 -070083 @Override
84 public void onTrimMemory(int level) {
85 if (mEngine != null) {
86 mEngine.trimMemory(level);
87 }
88 }
89
Romain Guy043a6b12011-09-27 15:37:54 -070090 private static boolean isEmulator() {
91 return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
92 }
93
Craig Mautnerb1ef3692012-11-16 17:31:04 -080094 @Override
Romain Guyef654bd2009-08-11 19:12:17 -070095 public Engine onCreateEngine() {
Chet Haasec61d70e2012-10-10 15:41:57 -070096 mEngine = new DrawableEngine();
97 return mEngine;
Romain Guyef654bd2009-08-11 19:12:17 -070098 }
99
100 class DrawableEngine extends Engine {
Romain Guy407ec782011-08-24 17:06:58 -0700101 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
102 static final int EGL_OPENGL_ES2_BIT = 4;
103
Romain Guy407ec782011-08-24 17:06:58 -0700104 // TODO: Not currently used, keeping around until we know we don't need it
105 @SuppressWarnings({"UnusedDeclaration"})
Dianne Hackborn284ac932009-08-28 10:34:25 -0700106 private WallpaperObserver mReceiver;
Romain Guy407ec782011-08-24 17:06:58 -0700107
108 Bitmap mBackground;
Chet Haase5f0d9762012-10-18 12:01:34 -0700109 int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
Chet Haase2f200812012-10-11 12:49:08 -0700110 int mLastRotation = -1;
Dianne Hackborn7341d7a2009-08-14 11:37:52 -0700111 float mXOffset;
112 float mYOffset;
Romain Guyef654bd2009-08-11 19:12:17 -0700113
Jeff Brownfa2e5042011-01-23 13:14:23 -0800114 boolean mVisible = true;
115 boolean mRedrawNeeded;
116 boolean mOffsetsChanged;
117 int mLastXTranslation;
118 int mLastYTranslation;
119
Romain Guy407ec782011-08-24 17:06:58 -0700120 private EGL10 mEgl;
121 private EGLDisplay mEglDisplay;
122 private EGLConfig mEglConfig;
123 private EGLContext mEglContext;
124 private EGLSurface mEglSurface;
Romain Guy407ec782011-08-24 17:06:58 -0700125
126 private static final String sSimpleVS =
127 "attribute vec4 position;\n" +
128 "attribute vec2 texCoords;\n" +
129 "varying vec2 outTexCoords;\n" +
130 "uniform mat4 projection;\n" +
131 "\nvoid main(void) {\n" +
132 " outTexCoords = texCoords;\n" +
133 " gl_Position = projection * position;\n" +
134 "}\n\n";
135 private static final String sSimpleFS =
136 "precision mediump float;\n\n" +
137 "varying vec2 outTexCoords;\n" +
138 "uniform sampler2D texture;\n" +
139 "\nvoid main(void) {\n" +
140 " gl_FragColor = texture2D(texture, outTexCoords);\n" +
141 "}\n\n";
142
143 private static final int FLOAT_SIZE_BYTES = 4;
144 private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
145 private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
146 private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
147
Dianne Hackborn284ac932009-08-28 10:34:25 -0700148 class WallpaperObserver extends BroadcastReceiver {
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800149 @Override
Dianne Hackborn284ac932009-08-28 10:34:25 -0700150 public void onReceive(Context context, Intent intent) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800151 if (DEBUG) {
152 Log.d(TAG, "onReceive");
153 }
154
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800155 mLastSurfaceWidth = mLastSurfaceHeight = -1;
156 mBackground = null;
157 mRedrawNeeded = true;
158 drawFrame();
Dianne Hackborn284ac932009-08-28 10:34:25 -0700159 }
160 }
161
Jeff Sharkey35be7562012-04-18 19:16:15 -0700162 public DrawableEngine() {
163 super();
164 setFixedSizeAllowed(true);
165 }
166
Chet Haasec61d70e2012-10-10 15:41:57 -0700167 public void trimMemory(int level) {
168 if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW &&
169 mBackground != null && mIsHwAccelerated) {
Chet Haase5f0d9762012-10-18 12:01:34 -0700170 if (DEBUG) {
171 Log.d(TAG, "trimMemory");
172 }
Chet Haasec61d70e2012-10-10 15:41:57 -0700173 mBackground.recycle();
174 mBackground = null;
175 mWallpaperManager.forgetLoadedWallpaper();
176 }
177 }
178
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700179 @Override
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700180 public void onCreate(SurfaceHolder surfaceHolder) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800181 if (DEBUG) {
182 Log.d(TAG, "onCreate");
183 }
184
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700185 super.onCreate(surfaceHolder);
Dianne Hackborn9ea31632011-08-05 14:43:50 -0700186
Romain Guy407ec782011-08-24 17:06:58 -0700187 // TODO: Don't need this currently because the wallpaper service
Dianne Hackborn9ea31632011-08-05 14:43:50 -0700188 // will restart the image wallpaper whenever the image changes.
189 //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
190 //mReceiver = new WallpaperObserver();
191 //registerReceiver(mReceiver, filter, null, mHandler);
Jeff Brownfa2e5042011-01-23 13:14:23 -0800192
Dianne Hackbornac1471a2011-02-03 13:46:06 -0800193 updateSurfaceSize(surfaceHolder);
Chet Haasea8e5a2b2011-10-28 13:18:16 -0700194
195 setOffsetNotificationsEnabled(false);
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700196 }
197
198 @Override
Dianne Hackborn284ac932009-08-28 10:34:25 -0700199 public void onDestroy() {
200 super.onDestroy();
Dianne Hackborn9ea31632011-08-05 14:43:50 -0700201 if (mReceiver != null) {
202 unregisterReceiver(mReceiver);
203 }
Dianne Hackborn284ac932009-08-28 10:34:25 -0700204 }
205
206 @Override
Dianne Hackbornac1471a2011-02-03 13:46:06 -0800207 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
Dianne Hackborn912d9d12011-02-04 10:35:36 -0800208 super.onDesiredSizeChanged(desiredWidth, desiredHeight);
Dianne Hackbornac1471a2011-02-03 13:46:06 -0800209 SurfaceHolder surfaceHolder = getSurfaceHolder();
210 if (surfaceHolder != null) {
211 updateSurfaceSize(surfaceHolder);
212 }
213 }
214
215 void updateSurfaceSize(SurfaceHolder surfaceHolder) {
Dianne Hackbornba398392011-08-01 16:11:57 -0700216 if (FIXED_SIZED_SURFACE) {
217 // Used a fixed size surface, because we are special. We can do
218 // this because we know the current design of window animations doesn't
219 // cause this to break.
220 surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
221 } else {
222 surfaceHolder.setSizeFromLayout();
223 }
Dianne Hackbornac1471a2011-02-03 13:46:06 -0800224 }
225
226 @Override
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700227 public void onVisibilityChanged(boolean visible) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800228 if (DEBUG) {
Chet Haase2f200812012-10-11 12:49:08 -0700229 Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible);
Jeff Brown30bc34f2011-01-25 12:56:56 -0800230 }
231
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800232 if (mVisible != visible) {
233 if (DEBUG) {
234 Log.d(TAG, "Visibility changed to visible=" + visible);
Jeff Brown30bc34f2011-01-25 12:56:56 -0800235 }
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800236 mVisible = visible;
237 drawFrame();
Jeff Brownfa2e5042011-01-23 13:14:23 -0800238 }
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700239 }
Jeff Brownfa2e5042011-01-23 13:14:23 -0800240
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700241 @Override
Dianne Hackborn8df8b2b2009-08-17 15:15:18 -0700242 public void onTouchEvent(MotionEvent event) {
243 super.onTouchEvent(event);
Dianne Hackborn8df8b2b2009-08-17 15:15:18 -0700244 }
245
246 @Override
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700247 public void onOffsetsChanged(float xOffset, float yOffset,
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800248 float xOffsetStep, float yOffsetStep,
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700249 int xPixels, int yPixels) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800250 if (DEBUG) {
251 Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
252 + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
253 + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
254 }
255
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800256 if (mXOffset != xOffset || mYOffset != yOffset) {
257 if (DEBUG) {
258 Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
Jeff Brownfa2e5042011-01-23 13:14:23 -0800259 }
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800260 mXOffset = xOffset;
261 mYOffset = yOffset;
262 mOffsetsChanged = true;
Jeff Brownfa2e5042011-01-23 13:14:23 -0800263 }
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800264 drawFrame();
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700265 }
266
267 @Override
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700268 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800269 if (DEBUG) {
270 Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
271 }
272
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700273 super.onSurfaceChanged(holder, format, width, height);
Jeff Brown30bc34f2011-01-25 12:56:56 -0800274
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800275 drawFrame();
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700276 }
277
Craig Mautnerfb729c72012-10-01 09:39:43 -0700278 @Override
Chet Haase2f200812012-10-11 12:49:08 -0700279 public void onSurfaceDestroyed(SurfaceHolder holder) {
280 super.onSurfaceDestroyed(holder);
Chet Haase5f0d9762012-10-18 12:01:34 -0700281 mLastSurfaceWidth = mLastSurfaceHeight = -1;
Chet Haase2f200812012-10-11 12:49:08 -0700282 }
283
284 @Override
285 public void onSurfaceCreated(SurfaceHolder holder) {
286 super.onSurfaceCreated(holder);
Chet Haase5f0d9762012-10-18 12:01:34 -0700287 mLastSurfaceWidth = mLastSurfaceHeight = -1;
Chet Haase2f200812012-10-11 12:49:08 -0700288 }
289
290 @Override
Craig Mautnerfb729c72012-10-01 09:39:43 -0700291 public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
292 if (DEBUG) {
Chet Haase2f200812012-10-11 12:49:08 -0700293 Log.d(TAG, "onSurfaceRedrawNeeded");
Craig Mautnerfb729c72012-10-01 09:39:43 -0700294 }
295 super.onSurfaceRedrawNeeded(holder);
296
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800297 drawFrame();
Craig Mautnerfb729c72012-10-01 09:39:43 -0700298 }
299
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800300 void drawFrame() {
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700301 SurfaceHolder sh = getSurfaceHolder();
Jeff Brown033f63a2011-01-23 22:01:49 -0800302 final Rect frame = sh.getSurfaceFrame();
Jeff Brown033f63a2011-01-23 22:01:49 -0800303 final int dw = frame.width();
304 final int dh = frame.height();
Chet Haase2f200812012-10-11 12:49:08 -0700305 int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
306 getDefaultDisplay().getRotation();
Chet Haase5f0d9762012-10-18 12:01:34 -0700307 boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight;
Chet Haase2f200812012-10-11 12:49:08 -0700308
Chet Haase5f0d9762012-10-18 12:01:34 -0700309 boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
Chet Haase2f200812012-10-11 12:49:08 -0700310 if (!redrawNeeded && !mOffsetsChanged) {
311 if (DEBUG) {
312 Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
313 + "and offsets have not changed.");
314 }
315 return;
316 }
317 mLastRotation = newRotation;
318
319 // Load bitmap if it is not yet loaded or if it was loaded at a different size
Chet Haase5f0d9762012-10-18 12:01:34 -0700320 if (mBackground == null || surfaceDimensionsChanged) {
Chet Haase2f200812012-10-11 12:49:08 -0700321 if (DEBUG) {
Chet Haase5f0d9762012-10-18 12:01:34 -0700322 Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
323 mBackground + ", " +
324 ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
325 ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
326 dw + ", " + dh);
Chet Haase2f200812012-10-11 12:49:08 -0700327 }
Chet Haase2f200812012-10-11 12:49:08 -0700328 updateWallpaperLocked();
Chet Haase5f0d9762012-10-18 12:01:34 -0700329 if (mBackground == null) {
330 if (DEBUG) {
331 Log.d(TAG, "Unable to load bitmap");
332 }
333 return;
334 }
335 if (DEBUG) {
336 if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
337 Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
338 dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
339 mBackground.getHeight());
340 }
341 }
Chet Haase2f200812012-10-11 12:49:08 -0700342 }
343
Chet Haase5f0d9762012-10-18 12:01:34 -0700344 final int availw = dw - mBackground.getWidth();
345 final int availh = dh - mBackground.getHeight();
Jeff Brown033f63a2011-01-23 22:01:49 -0800346 int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
347 int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
348
349 mOffsetsChanged = false;
Chet Haase2f200812012-10-11 12:49:08 -0700350 mRedrawNeeded = false;
Chet Haase5f0d9762012-10-18 12:01:34 -0700351 if (surfaceDimensionsChanged) {
352 mLastSurfaceWidth = dw;
353 mLastSurfaceHeight = dh;
354 }
Chet Haase2f200812012-10-11 12:49:08 -0700355 mLastXTranslation = xPixels;
356 mLastYTranslation = yPixels;
357 if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
Jeff Brown033f63a2011-01-23 22:01:49 -0800358 if (DEBUG) {
359 Log.d(TAG, "Suppressed drawFrame since the image has not "
360 + "actually moved an integral number of pixels.");
361 }
362 return;
363 }
Jeff Brown033f63a2011-01-23 22:01:49 -0800364
Craig Mautnerc92f1502012-10-13 15:40:28 -0700365 if (DEBUG) {
Chet Haase2f200812012-10-11 12:49:08 -0700366 Log.d(TAG, "Redrawing wallpaper");
Craig Mautnerc92f1502012-10-13 15:40:28 -0700367 }
Wim Vander Schelden9549c062013-02-07 15:51:51 +0000368
Romain Guy407ec782011-08-24 17:06:58 -0700369 if (mIsHwAccelerated) {
Romain Guyf9296292011-08-25 17:00:39 -0700370 if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
371 drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
372 }
Romain Guy407ec782011-08-24 17:06:58 -0700373 } else {
374 drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
Chet Haasec61d70e2012-10-10 15:41:57 -0700375 if (FIXED_SIZED_SURFACE) {
376 // If the surface is fixed-size, we should only need to
377 // draw it once and then we'll let the window manager
378 // position it appropriately. As such, we no longer needed
379 // the loaded bitmap. Yay!
380 // hw-accelerated path retains bitmap for faster rotation
381 mBackground = null;
382 mWallpaperManager.forgetLoadedWallpaper();
383 }
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700384 }
Dianne Hackbornba398392011-08-01 16:11:57 -0700385
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700386 }
Romain Guyef654bd2009-08-11 19:12:17 -0700387
Chet Haase5f0d9762012-10-18 12:01:34 -0700388 private void updateWallpaperLocked() {
Jeff Brownfa2e5042011-01-23 13:14:23 -0800389 Throwable exception = null;
390 try {
Chet Haase589a6af2012-10-24 17:37:00 -0700391 mBackground = null;
Romain Guy407ec782011-08-24 17:06:58 -0700392 mBackground = mWallpaperManager.getBitmap();
Jeff Brownfa2e5042011-01-23 13:14:23 -0800393 } catch (RuntimeException e) {
394 exception = e;
395 } catch (OutOfMemoryError e) {
396 exception = e;
397 }
Romain Guy407ec782011-08-24 17:06:58 -0700398
Jeff Brownfa2e5042011-01-23 13:14:23 -0800399 if (exception != null) {
400 mBackground = null;
401 // Note that if we do fail at this, and the default wallpaper can't
402 // be loaded, we will go into a cycle. Don't do a build where the
403 // default wallpaper can't be loaded.
404 Log.w(TAG, "Unable to load wallpaper!", exception);
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800405 try {
Jeff Brownfa2e5042011-01-23 13:14:23 -0800406 mWallpaperManager.clear();
407 } catch (IOException ex) {
408 // now we're really screwed.
409 Log.w(TAG, "Unable reset to default wallpaper!", ex);
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800410 }
Romain Guyef654bd2009-08-11 19:12:17 -0700411 }
Romain Guy407ec782011-08-24 17:06:58 -0700412 }
413
414 private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
415 Canvas c = sh.lockCanvas();
416 if (c != null) {
417 try {
418 if (DEBUG) {
419 Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
420 }
421
422 c.translate(x, y);
423 if (w < 0 || h < 0) {
424 c.save(Canvas.CLIP_SAVE_FLAG);
Chet Haase5f0d9762012-10-18 12:01:34 -0700425 c.clipRect(0, 0, mBackground.getWidth(), mBackground.getHeight(),
426 Op.DIFFERENCE);
Romain Guy407ec782011-08-24 17:06:58 -0700427 c.drawColor(0xff000000);
428 c.restore();
429 }
430 if (mBackground != null) {
431 c.drawBitmap(mBackground, 0, 0, null);
432 }
433 } finally {
434 sh.unlockCanvasAndPost(c);
435 }
436 }
437 }
438
Romain Guyf9296292011-08-25 17:00:39 -0700439 private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
440 if (!initGL(sh)) return false;
Romain Guy407ec782011-08-24 17:06:58 -0700441
Chet Haase5f0d9762012-10-18 12:01:34 -0700442 final float right = left + mBackground.getWidth();
443 final float bottom = top + mBackground.getHeight();
Romain Guy407ec782011-08-24 17:06:58 -0700444
445 final Rect frame = sh.getSurfaceFrame();
Romain Guy407ec782011-08-24 17:06:58 -0700446 final Matrix4f ortho = new Matrix4f();
447 ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
448
449 final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
450
451 final int texture = loadTexture(mBackground);
452 final int program = buildProgram(sSimpleVS, sSimpleFS);
Chet Haase2f200812012-10-11 12:49:08 -0700453
Romain Guy407ec782011-08-24 17:06:58 -0700454 final int attribPosition = glGetAttribLocation(program, "position");
455 final int attribTexCoords = glGetAttribLocation(program, "texCoords");
456 final int uniformTexture = glGetUniformLocation(program, "texture");
457 final int uniformProjection = glGetUniformLocation(program, "projection");
458
459 checkGlError();
460
461 glViewport(0, 0, frame.width(), frame.height());
462 glBindTexture(GL_TEXTURE_2D, texture);
463
464 glUseProgram(program);
465 glEnableVertexAttribArray(attribPosition);
466 glEnableVertexAttribArray(attribTexCoords);
467 glUniform1i(uniformTexture, 0);
468 glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
469
470 checkGlError();
471
472 if (w < 0 || h < 0) {
473 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
474 glClear(GL_COLOR_BUFFER_BIT);
475 }
Chet Haase2f200812012-10-11 12:49:08 -0700476
Romain Guy407ec782011-08-24 17:06:58 -0700477 // drawQuad
478 triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
479 glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
480 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
481
482 triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
483 glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
484 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
485
486 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Chet Haase2f200812012-10-11 12:49:08 -0700487
Romain Guyc8d983f2013-02-20 10:05:36 -0800488 boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
Romain Guy407ec782011-08-24 17:06:58 -0700489 checkEglError();
Chet Haase2f200812012-10-11 12:49:08 -0700490
Romain Guy407ec782011-08-24 17:06:58 -0700491 finishGL();
Romain Guyf9296292011-08-25 17:00:39 -0700492
Romain Guyc8d983f2013-02-20 10:05:36 -0800493 return status;
Romain Guy407ec782011-08-24 17:06:58 -0700494 }
495
496 private FloatBuffer createMesh(int left, int top, float right, float bottom) {
497 final float[] verticesData = {
498 // X, Y, Z, U, V
499 left, bottom, 0.0f, 0.0f, 1.0f,
500 right, bottom, 0.0f, 1.0f, 1.0f,
501 left, top, 0.0f, 0.0f, 0.0f,
502 right, top, 0.0f, 1.0f, 0.0f,
503 };
504
505 final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
506 final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
507 ByteOrder.nativeOrder()).asFloatBuffer();
508 triangleVertices.put(verticesData).position(0);
509 return triangleVertices;
510 }
511
512 private int loadTexture(Bitmap bitmap) {
513 int[] textures = new int[1];
514
515 glActiveTexture(GL_TEXTURE0);
516 glGenTextures(1, textures, 0);
517 checkGlError();
518
519 int texture = textures[0];
520 glBindTexture(GL_TEXTURE_2D, texture);
521 checkGlError();
522
523 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
524 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
525
526 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
527 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
528
529 GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
530 checkGlError();
531
Romain Guy407ec782011-08-24 17:06:58 -0700532 return texture;
533 }
534
535 private int buildProgram(String vertex, String fragment) {
536 int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
537 if (vertexShader == 0) return 0;
538
539 int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
540 if (fragmentShader == 0) return 0;
541
542 int program = glCreateProgram();
543 glAttachShader(program, vertexShader);
544 checkGlError();
545
546 glAttachShader(program, fragmentShader);
547 checkGlError();
548
549 glLinkProgram(program);
550 checkGlError();
551
552 int[] status = new int[1];
553 glGetProgramiv(program, GL_LINK_STATUS, status, 0);
554 if (status[0] != GL_TRUE) {
555 String error = glGetProgramInfoLog(program);
556 Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
557 glDeleteShader(vertexShader);
558 glDeleteShader(fragmentShader);
559 glDeleteProgram(program);
560 return 0;
561 }
562
563 return program;
564 }
Romain Guy3696779b2013-01-28 14:04:07 -0800565
Romain Guy407ec782011-08-24 17:06:58 -0700566 private int buildShader(String source, int type) {
567 int shader = glCreateShader(type);
568
569 glShaderSource(shader, source);
570 checkGlError();
571
572 glCompileShader(shader);
573 checkGlError();
574
575 int[] status = new int[1];
576 glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
577 if (status[0] != GL_TRUE) {
578 String error = glGetShaderInfoLog(shader);
579 Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
580 glDeleteShader(shader);
581 return 0;
582 }
583
584 return shader;
585 }
Romain Guy3696779b2013-01-28 14:04:07 -0800586
Romain Guy407ec782011-08-24 17:06:58 -0700587 private void checkEglError() {
588 int error = mEgl.eglGetError();
589 if (error != EGL_SUCCESS) {
590 Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
591 }
592 }
Romain Guy3696779b2013-01-28 14:04:07 -0800593
Romain Guy407ec782011-08-24 17:06:58 -0700594 private void checkGlError() {
595 int error = glGetError();
596 if (error != GL_NO_ERROR) {
597 Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
598 }
599 }
Romain Guy3696779b2013-01-28 14:04:07 -0800600
Romain Guy407ec782011-08-24 17:06:58 -0700601 private void finishGL() {
Romain Guyf9296292011-08-25 17:00:39 -0700602 mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
Romain Guy407ec782011-08-24 17:06:58 -0700603 mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
Romain Guyf9296292011-08-25 17:00:39 -0700604 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
Romain Guy3696779b2013-01-28 14:04:07 -0800605 mEgl.eglTerminate(mEglDisplay);
Romain Guy407ec782011-08-24 17:06:58 -0700606 }
Romain Guy3696779b2013-01-28 14:04:07 -0800607
Romain Guyf9296292011-08-25 17:00:39 -0700608 private boolean initGL(SurfaceHolder surfaceHolder) {
Romain Guy407ec782011-08-24 17:06:58 -0700609 mEgl = (EGL10) EGLContext.getEGL();
610
611 mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
612 if (mEglDisplay == EGL_NO_DISPLAY) {
613 throw new RuntimeException("eglGetDisplay failed " +
614 GLUtils.getEGLErrorString(mEgl.eglGetError()));
615 }
616
617 int[] version = new int[2];
618 if (!mEgl.eglInitialize(mEglDisplay, version)) {
619 throw new RuntimeException("eglInitialize failed " +
620 GLUtils.getEGLErrorString(mEgl.eglGetError()));
621 }
622
623 mEglConfig = chooseEglConfig();
624 if (mEglConfig == null) {
625 throw new RuntimeException("eglConfig not initialized");
626 }
627
628 mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
Chet Haase187e1e22013-03-13 18:04:00 -0700629 if (mEglContext == EGL_NO_CONTEXT) {
630 throw new RuntimeException("createContext failed " +
631 GLUtils.getEGLErrorString(mEgl.eglGetError()));
Wim Vander Schelden9549c062013-02-07 15:51:51 +0000632 }
Chet Haase187e1e22013-03-13 18:04:00 -0700633
Chet Haase2e417be2013-03-14 09:28:42 -0700634 int attribs[] = {
635 EGL_WIDTH, 1,
636 EGL_HEIGHT, 1,
637 EGL_NONE
638 };
639 EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
640 mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
641
642 int[] maxSize = new int[1];
643 Rect frame = surfaceHolder.getSurfaceFrame();
644 glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0);
645
646 mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
647 mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
648
649 if(frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
650 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
651 mEgl.eglTerminate(mEglDisplay);
652 Log.e(GL_LOG_TAG, "requested texture size " +
653 frame.width() + "x" + frame.height() + " exceeds the support maximum of " +
654 maxSize[0] + "x" + maxSize[0]);
655 return false;
656 }
657
Romain Guy407ec782011-08-24 17:06:58 -0700658 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
Romain Guy407ec782011-08-24 17:06:58 -0700659 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
660 int error = mEgl.eglGetError();
Wim Vander Schelden9549c062013-02-07 15:51:51 +0000661 if (error == EGL_BAD_NATIVE_WINDOW || error == EGL_BAD_ALLOC) {
662 Log.e(GL_LOG_TAG, "createWindowSurface returned " +
663 GLUtils.getEGLErrorString(error) + ".");
Romain Guyf9296292011-08-25 17:00:39 -0700664 return false;
Romain Guy407ec782011-08-24 17:06:58 -0700665 }
666 throw new RuntimeException("createWindowSurface failed " +
667 GLUtils.getEGLErrorString(error));
668 }
669
670 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
671 throw new RuntimeException("eglMakeCurrent failed " +
672 GLUtils.getEGLErrorString(mEgl.eglGetError()));
673 }
Romain Guyf9296292011-08-25 17:00:39 -0700674
675 return true;
Romain Guy407ec782011-08-24 17:06:58 -0700676 }
677
678
679 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
680 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
681 return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
682 }
683
684 private EGLConfig chooseEglConfig() {
685 int[] configsCount = new int[1];
686 EGLConfig[] configs = new EGLConfig[1];
687 int[] configSpec = getConfig();
688 if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
689 throw new IllegalArgumentException("eglChooseConfig failed " +
690 GLUtils.getEGLErrorString(mEgl.eglGetError()));
691 } else if (configsCount[0] > 0) {
692 return configs[0];
693 }
694 return null;
695 }
696
697 private int[] getConfig() {
698 return new int[] {
699 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
700 EGL_RED_SIZE, 8,
701 EGL_GREEN_SIZE, 8,
702 EGL_BLUE_SIZE, 8,
703 EGL_ALPHA_SIZE, 0,
704 EGL_DEPTH_SIZE, 0,
705 EGL_STENCIL_SIZE, 0,
Romain Guy8efca542012-10-15 18:09:49 -0700706 EGL_CONFIG_CAVEAT, EGL_NONE,
Romain Guy407ec782011-08-24 17:06:58 -0700707 EGL_NONE
708 };
Romain Guyef654bd2009-08-11 19:12:17 -0700709 }
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700710 }
711}