blob: 6fa863db93f34b6e66277bac9a34efed4d7e1685 [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
John Spurlockde84f0e2013-06-12 12:41:00 -040019import static android.opengl.GLES20.*;
20import static javax.microedition.khronos.egl.EGL10.*;
21
Romain Guy407ec782011-08-24 17:06:58 -070022import android.app.ActivityManager;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070023import android.app.WallpaperManager;
Romain Guy407ec782011-08-24 17:06:58 -070024import android.content.BroadcastReceiver;
Chet Haasec61d70e2012-10-10 15:41:57 -070025import android.content.ComponentCallbacks2;
Romain Guy407ec782011-08-24 17:06:58 -070026import android.content.Context;
27import android.content.Intent;
28import android.graphics.Bitmap;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070029import android.graphics.Canvas;
Dianne Hackborn759a39e2009-08-09 17:20:27 -070030import android.graphics.Rect;
Mathias Agopiane2d034c2009-09-23 21:06:17 -070031import android.graphics.Region.Op;
Romain Guy407ec782011-08-24 17:06:58 -070032import android.opengl.GLUtils;
Romain Guy043a6b12011-09-27 15:37:54 -070033import android.os.SystemProperties;
Romain Guy407ec782011-08-24 17:06:58 -070034import android.renderscript.Matrix4f;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070035import android.service.wallpaper.WallpaperService;
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -080036import android.util.Log;
Dianne Hackborn8df8b2b2009-08-17 15:15:18 -070037import android.view.MotionEvent;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070038import android.view.SurfaceHolder;
Chet Haase2f200812012-10-11 12:49:08 -070039import android.view.WindowManager;
Romain Guy407ec782011-08-24 17:06:58 -070040
Romain Guy407ec782011-08-24 17:06:58 -070041import java.io.IOException;
42import java.nio.ByteBuffer;
43import java.nio.ByteOrder;
44import java.nio.FloatBuffer;
45
John Spurlockde84f0e2013-06-12 12:41:00 -040046import javax.microedition.khronos.egl.EGL10;
47import javax.microedition.khronos.egl.EGLConfig;
48import javax.microedition.khronos.egl.EGLContext;
49import javax.microedition.khronos.egl.EGLDisplay;
50import javax.microedition.khronos.egl.EGLSurface;
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070051
52/**
53 * Default built-in wallpaper that simply shows a static image.
54 */
Romain Guy407ec782011-08-24 17:06:58 -070055@SuppressWarnings({"UnusedDeclaration"})
Dianne Hackborn4c62fc02009-08-08 20:40:27 -070056public class ImageWallpaper extends WallpaperService {
Jeff Brownfa2e5042011-01-23 13:14:23 -080057 private static final String TAG = "ImageWallpaper";
Romain Guy407ec782011-08-24 17:06:58 -070058 private static final String GL_LOG_TAG = "ImageWallpaperGL";
Jeff Brownfa2e5042011-01-23 13:14:23 -080059 private static final boolean DEBUG = false;
Romain Guy043a6b12011-09-27 15:37:54 -070060 private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
Jeff Brownfa2e5042011-01-23 13:14:23 -080061
Dianne Hackbornba398392011-08-01 16:11:57 -070062 static final boolean FIXED_SIZED_SURFACE = true;
Erik Gilling881fb202011-08-25 10:27:52 -070063 static final boolean USE_OPENGL = true;
Dianne Hackbornba398392011-08-01 16:11:57 -070064
Romain Guyef654bd2009-08-11 19:12:17 -070065 WallpaperManager mWallpaperManager;
Romain Guy407ec782011-08-24 17:06:58 -070066
Chet Haasec61d70e2012-10-10 15:41:57 -070067 DrawableEngine mEngine;
68
Romain Guy407ec782011-08-24 17:06:58 -070069 boolean mIsHwAccelerated;
Dianne Hackborn759a39e2009-08-09 17:20:27 -070070
Romain Guyef654bd2009-08-11 19:12:17 -070071 @Override
72 public void onCreate() {
73 super.onCreate();
74 mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
Romain Guy407ec782011-08-24 17:06:58 -070075
76 //noinspection PointlessBooleanExpression,ConstantConditions
77 if (FIXED_SIZED_SURFACE && USE_OPENGL) {
Romain Guy043a6b12011-09-27 15:37:54 -070078 if (!isEmulator()) {
Jeff Brown98365d72012-08-19 20:30:52 -070079 mIsHwAccelerated = ActivityManager.isHighEndGfx();
Romain Guy043a6b12011-09-27 15:37:54 -070080 }
Romain Guy407ec782011-08-24 17:06:58 -070081 }
Romain Guyef654bd2009-08-11 19:12:17 -070082 }
83
Chet Haasec61d70e2012-10-10 15:41:57 -070084 @Override
85 public void onTrimMemory(int level) {
86 if (mEngine != null) {
87 mEngine.trimMemory(level);
88 }
89 }
90
Romain Guy043a6b12011-09-27 15:37:54 -070091 private static boolean isEmulator() {
92 return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
93 }
94
Craig Mautnerb1ef3692012-11-16 17:31:04 -080095 @Override
Romain Guyef654bd2009-08-11 19:12:17 -070096 public Engine onCreateEngine() {
Chet Haasec61d70e2012-10-10 15:41:57 -070097 mEngine = new DrawableEngine();
98 return mEngine;
Romain Guyef654bd2009-08-11 19:12:17 -070099 }
100
101 class DrawableEngine extends Engine {
Romain Guy407ec782011-08-24 17:06:58 -0700102 static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
103 static final int EGL_OPENGL_ES2_BIT = 4;
104
Romain Guy407ec782011-08-24 17:06:58 -0700105 // TODO: Not currently used, keeping around until we know we don't need it
106 @SuppressWarnings({"UnusedDeclaration"})
Dianne Hackborn284ac932009-08-28 10:34:25 -0700107 private WallpaperObserver mReceiver;
Romain Guy407ec782011-08-24 17:06:58 -0700108
109 Bitmap mBackground;
Chet Haase5f0d9762012-10-18 12:01:34 -0700110 int mLastSurfaceWidth = -1, mLastSurfaceHeight = -1;
Chet Haase2f200812012-10-11 12:49:08 -0700111 int mLastRotation = -1;
Dianne Hackborn7341d7a2009-08-14 11:37:52 -0700112 float mXOffset;
113 float mYOffset;
Romain Guyef654bd2009-08-11 19:12:17 -0700114
Jeff Brownfa2e5042011-01-23 13:14:23 -0800115 boolean mVisible = true;
116 boolean mRedrawNeeded;
117 boolean mOffsetsChanged;
118 int mLastXTranslation;
119 int mLastYTranslation;
120
Romain Guy407ec782011-08-24 17:06:58 -0700121 private EGL10 mEgl;
122 private EGLDisplay mEglDisplay;
123 private EGLConfig mEglConfig;
124 private EGLContext mEglContext;
125 private EGLSurface mEglSurface;
Romain Guy407ec782011-08-24 17:06:58 -0700126
127 private static final String sSimpleVS =
128 "attribute vec4 position;\n" +
129 "attribute vec2 texCoords;\n" +
130 "varying vec2 outTexCoords;\n" +
131 "uniform mat4 projection;\n" +
132 "\nvoid main(void) {\n" +
133 " outTexCoords = texCoords;\n" +
134 " gl_Position = projection * position;\n" +
135 "}\n\n";
136 private static final String sSimpleFS =
137 "precision mediump float;\n\n" +
138 "varying vec2 outTexCoords;\n" +
139 "uniform sampler2D texture;\n" +
140 "\nvoid main(void) {\n" +
141 " gl_FragColor = texture2D(texture, outTexCoords);\n" +
142 "}\n\n";
John Spurlock209bede2013-07-17 12:23:27 -0400143
Romain Guy407ec782011-08-24 17:06:58 -0700144 private static final int FLOAT_SIZE_BYTES = 4;
145 private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
146 private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
147 private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
148
Dianne Hackborn284ac932009-08-28 10:34:25 -0700149 class WallpaperObserver extends BroadcastReceiver {
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800150 @Override
Dianne Hackborn284ac932009-08-28 10:34:25 -0700151 public void onReceive(Context context, Intent intent) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800152 if (DEBUG) {
153 Log.d(TAG, "onReceive");
154 }
155
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800156 mLastSurfaceWidth = mLastSurfaceHeight = -1;
157 mBackground = null;
158 mRedrawNeeded = true;
159 drawFrame();
Dianne Hackborn284ac932009-08-28 10:34:25 -0700160 }
161 }
162
Jeff Sharkey35be7562012-04-18 19:16:15 -0700163 public DrawableEngine() {
164 super();
165 setFixedSizeAllowed(true);
166 }
167
Chet Haasec61d70e2012-10-10 15:41:57 -0700168 public void trimMemory(int level) {
169 if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW &&
170 mBackground != null && mIsHwAccelerated) {
Chet Haase5f0d9762012-10-18 12:01:34 -0700171 if (DEBUG) {
172 Log.d(TAG, "trimMemory");
173 }
Chet Haasec61d70e2012-10-10 15:41:57 -0700174 mBackground.recycle();
175 mBackground = null;
176 mWallpaperManager.forgetLoadedWallpaper();
177 }
178 }
179
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700180 @Override
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700181 public void onCreate(SurfaceHolder surfaceHolder) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800182 if (DEBUG) {
183 Log.d(TAG, "onCreate");
184 }
185
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700186 super.onCreate(surfaceHolder);
John Spurlock209bede2013-07-17 12:23:27 -0400187
Romain Guy407ec782011-08-24 17:06:58 -0700188 // TODO: Don't need this currently because the wallpaper service
Dianne Hackborn9ea31632011-08-05 14:43:50 -0700189 // will restart the image wallpaper whenever the image changes.
190 //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
191 //mReceiver = new WallpaperObserver();
192 //registerReceiver(mReceiver, filter, null, mHandler);
Jeff Brownfa2e5042011-01-23 13:14:23 -0800193
Dianne Hackbornac1471a2011-02-03 13:46:06 -0800194 updateSurfaceSize(surfaceHolder);
Chet Haasea8e5a2b2011-10-28 13:18:16 -0700195
196 setOffsetNotificationsEnabled(false);
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700197 }
198
199 @Override
Dianne Hackborn284ac932009-08-28 10:34:25 -0700200 public void onDestroy() {
201 super.onDestroy();
Dianne Hackborn9ea31632011-08-05 14:43:50 -0700202 if (mReceiver != null) {
203 unregisterReceiver(mReceiver);
204 }
Dianne Hackborn284ac932009-08-28 10:34:25 -0700205 }
206
207 @Override
Dianne Hackbornac1471a2011-02-03 13:46:06 -0800208 public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
Dianne Hackborn912d9d12011-02-04 10:35:36 -0800209 super.onDesiredSizeChanged(desiredWidth, desiredHeight);
Dianne Hackbornac1471a2011-02-03 13:46:06 -0800210 SurfaceHolder surfaceHolder = getSurfaceHolder();
211 if (surfaceHolder != null) {
212 updateSurfaceSize(surfaceHolder);
213 }
214 }
215
216 void updateSurfaceSize(SurfaceHolder surfaceHolder) {
Dianne Hackbornba398392011-08-01 16:11:57 -0700217 if (FIXED_SIZED_SURFACE) {
218 // Used a fixed size surface, because we are special. We can do
219 // this because we know the current design of window animations doesn't
220 // cause this to break.
221 surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
222 } else {
223 surfaceHolder.setSizeFromLayout();
224 }
Dianne Hackbornac1471a2011-02-03 13:46:06 -0800225 }
226
227 @Override
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700228 public void onVisibilityChanged(boolean visible) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800229 if (DEBUG) {
Chet Haase2f200812012-10-11 12:49:08 -0700230 Log.d(TAG, "onVisibilityChanged: mVisible, visible=" + mVisible + ", " + visible);
Jeff Brown30bc34f2011-01-25 12:56:56 -0800231 }
232
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800233 if (mVisible != visible) {
234 if (DEBUG) {
235 Log.d(TAG, "Visibility changed to visible=" + visible);
Jeff Brown30bc34f2011-01-25 12:56:56 -0800236 }
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800237 mVisible = visible;
238 drawFrame();
Jeff Brownfa2e5042011-01-23 13:14:23 -0800239 }
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700240 }
Jeff Brownfa2e5042011-01-23 13:14:23 -0800241
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700242 @Override
Dianne Hackborn8df8b2b2009-08-17 15:15:18 -0700243 public void onTouchEvent(MotionEvent event) {
244 super.onTouchEvent(event);
Dianne Hackborn8df8b2b2009-08-17 15:15:18 -0700245 }
246
247 @Override
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700248 public void onOffsetsChanged(float xOffset, float yOffset,
Marco Nelissenbf6956b2009-11-09 15:21:13 -0800249 float xOffsetStep, float yOffsetStep,
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700250 int xPixels, int yPixels) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800251 if (DEBUG) {
252 Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
253 + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
254 + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
255 }
256
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800257 if (mXOffset != xOffset || mYOffset != yOffset) {
258 if (DEBUG) {
259 Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
Jeff Brownfa2e5042011-01-23 13:14:23 -0800260 }
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800261 mXOffset = xOffset;
262 mYOffset = yOffset;
263 mOffsetsChanged = true;
Jeff Brownfa2e5042011-01-23 13:14:23 -0800264 }
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800265 drawFrame();
Dianne Hackborn72c82ab2009-08-11 21:13:54 -0700266 }
267
268 @Override
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700269 public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Jeff Brown30bc34f2011-01-25 12:56:56 -0800270 if (DEBUG) {
271 Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
272 }
273
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700274 super.onSurfaceChanged(holder, format, width, height);
Jeff Brown30bc34f2011-01-25 12:56:56 -0800275
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800276 drawFrame();
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700277 }
278
Craig Mautnerfb729c72012-10-01 09:39:43 -0700279 @Override
Chet Haase2f200812012-10-11 12:49:08 -0700280 public void onSurfaceDestroyed(SurfaceHolder holder) {
281 super.onSurfaceDestroyed(holder);
Chet Haase5f0d9762012-10-18 12:01:34 -0700282 mLastSurfaceWidth = mLastSurfaceHeight = -1;
Chet Haase2f200812012-10-11 12:49:08 -0700283 }
284
285 @Override
286 public void onSurfaceCreated(SurfaceHolder holder) {
287 super.onSurfaceCreated(holder);
Chet Haase5f0d9762012-10-18 12:01:34 -0700288 mLastSurfaceWidth = mLastSurfaceHeight = -1;
Chet Haase2f200812012-10-11 12:49:08 -0700289 }
290
291 @Override
Craig Mautnerfb729c72012-10-01 09:39:43 -0700292 public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
293 if (DEBUG) {
Chet Haase2f200812012-10-11 12:49:08 -0700294 Log.d(TAG, "onSurfaceRedrawNeeded");
Craig Mautnerfb729c72012-10-01 09:39:43 -0700295 }
296 super.onSurfaceRedrawNeeded(holder);
297
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800298 drawFrame();
Craig Mautnerfb729c72012-10-01 09:39:43 -0700299 }
300
Craig Mautnerb1ef3692012-11-16 17:31:04 -0800301 void drawFrame() {
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700302 SurfaceHolder sh = getSurfaceHolder();
Jeff Brown033f63a2011-01-23 22:01:49 -0800303 final Rect frame = sh.getSurfaceFrame();
Jeff Brown033f63a2011-01-23 22:01:49 -0800304 final int dw = frame.width();
305 final int dh = frame.height();
Chet Haase2f200812012-10-11 12:49:08 -0700306 int newRotation = ((WindowManager) getSystemService(WINDOW_SERVICE)).
307 getDefaultDisplay().getRotation();
Chet Haase5f0d9762012-10-18 12:01:34 -0700308 boolean surfaceDimensionsChanged = dw != mLastSurfaceWidth || dh != mLastSurfaceHeight;
Chet Haase2f200812012-10-11 12:49:08 -0700309
Chet Haase5f0d9762012-10-18 12:01:34 -0700310 boolean redrawNeeded = surfaceDimensionsChanged || newRotation != mLastRotation;
Chet Haase2f200812012-10-11 12:49:08 -0700311 if (!redrawNeeded && !mOffsetsChanged) {
312 if (DEBUG) {
313 Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
314 + "and offsets have not changed.");
315 }
316 return;
317 }
318 mLastRotation = newRotation;
319
320 // Load bitmap if it is not yet loaded or if it was loaded at a different size
Chet Haase5f0d9762012-10-18 12:01:34 -0700321 if (mBackground == null || surfaceDimensionsChanged) {
Chet Haase2f200812012-10-11 12:49:08 -0700322 if (DEBUG) {
Chet Haase5f0d9762012-10-18 12:01:34 -0700323 Log.d(TAG, "Reloading bitmap: mBackground, bgw, bgh, dw, dh = " +
324 mBackground + ", " +
325 ((mBackground == null) ? 0 : mBackground.getWidth()) + ", " +
326 ((mBackground == null) ? 0 : mBackground.getHeight()) + ", " +
327 dw + ", " + dh);
Chet Haase2f200812012-10-11 12:49:08 -0700328 }
Chet Haase2f200812012-10-11 12:49:08 -0700329 updateWallpaperLocked();
Chet Haase5f0d9762012-10-18 12:01:34 -0700330 if (mBackground == null) {
331 if (DEBUG) {
332 Log.d(TAG, "Unable to load bitmap");
333 }
334 return;
335 }
336 if (DEBUG) {
337 if (dw != mBackground.getWidth() || dh != mBackground.getHeight()) {
338 Log.d(TAG, "Surface != bitmap dimensions: surface w/h, bitmap w/h: " +
339 dw + ", " + dh + ", " + mBackground.getWidth() + ", " +
340 mBackground.getHeight());
341 }
342 }
Chet Haase2f200812012-10-11 12:49:08 -0700343 }
344
Chet Haase5f0d9762012-10-18 12:01:34 -0700345 final int availw = dw - mBackground.getWidth();
346 final int availh = dh - mBackground.getHeight();
Jeff Brown033f63a2011-01-23 22:01:49 -0800347 int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
348 int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
349
350 mOffsetsChanged = false;
Chet Haase2f200812012-10-11 12:49:08 -0700351 mRedrawNeeded = false;
Chet Haase5f0d9762012-10-18 12:01:34 -0700352 if (surfaceDimensionsChanged) {
353 mLastSurfaceWidth = dw;
354 mLastSurfaceHeight = dh;
355 }
Chet Haase2f200812012-10-11 12:49:08 -0700356 mLastXTranslation = xPixels;
357 mLastYTranslation = yPixels;
358 if (!redrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
Jeff Brown033f63a2011-01-23 22:01:49 -0800359 if (DEBUG) {
360 Log.d(TAG, "Suppressed drawFrame since the image has not "
361 + "actually moved an integral number of pixels.");
362 }
363 return;
364 }
Jeff Brown033f63a2011-01-23 22:01:49 -0800365
Craig Mautnerc92f1502012-10-13 15:40:28 -0700366 if (DEBUG) {
Chet Haase2f200812012-10-11 12:49:08 -0700367 Log.d(TAG, "Redrawing wallpaper");
Craig Mautnerc92f1502012-10-13 15:40:28 -0700368 }
Wim Vander Schelden9549c062013-02-07 15:51:51 +0000369
Romain Guy407ec782011-08-24 17:06:58 -0700370 if (mIsHwAccelerated) {
Romain Guyf9296292011-08-25 17:00:39 -0700371 if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
372 drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
373 }
Romain Guy407ec782011-08-24 17:06:58 -0700374 } else {
375 drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
Chet Haasec61d70e2012-10-10 15:41:57 -0700376 if (FIXED_SIZED_SURFACE) {
377 // If the surface is fixed-size, we should only need to
378 // draw it once and then we'll let the window manager
379 // position it appropriately. As such, we no longer needed
380 // the loaded bitmap. Yay!
381 // hw-accelerated path retains bitmap for faster rotation
382 mBackground = null;
383 mWallpaperManager.forgetLoadedWallpaper();
384 }
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700385 }
Dianne Hackbornba398392011-08-01 16:11:57 -0700386
Dianne Hackborn759a39e2009-08-09 17:20:27 -0700387 }
Romain Guyef654bd2009-08-11 19:12:17 -0700388
Chet Haase5f0d9762012-10-18 12:01:34 -0700389 private void updateWallpaperLocked() {
Jeff Brownfa2e5042011-01-23 13:14:23 -0800390 Throwable exception = null;
391 try {
Chet Haase589a6af2012-10-24 17:37:00 -0700392 mBackground = null;
Romain Guy407ec782011-08-24 17:06:58 -0700393 mBackground = mWallpaperManager.getBitmap();
Jeff Brownfa2e5042011-01-23 13:14:23 -0800394 } catch (RuntimeException e) {
395 exception = e;
396 } catch (OutOfMemoryError e) {
397 exception = e;
398 }
Romain Guy407ec782011-08-24 17:06:58 -0700399
Jeff Brownfa2e5042011-01-23 13:14:23 -0800400 if (exception != null) {
401 mBackground = null;
402 // Note that if we do fail at this, and the default wallpaper can't
403 // be loaded, we will go into a cycle. Don't do a build where the
404 // default wallpaper can't be loaded.
405 Log.w(TAG, "Unable to load wallpaper!", exception);
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800406 try {
Jeff Brownfa2e5042011-01-23 13:14:23 -0800407 mWallpaperManager.clear();
408 } catch (IOException ex) {
409 // now we're really screwed.
410 Log.w(TAG, "Unable reset to default wallpaper!", ex);
Dianne Hackbornc9dbbe22009-11-11 22:50:37 -0800411 }
Romain Guyef654bd2009-08-11 19:12:17 -0700412 }
Romain Guy407ec782011-08-24 17:06:58 -0700413 }
414
415 private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
416 Canvas c = sh.lockCanvas();
417 if (c != null) {
418 try {
419 if (DEBUG) {
420 Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
421 }
422
423 c.translate(x, y);
424 if (w < 0 || h < 0) {
425 c.save(Canvas.CLIP_SAVE_FLAG);
Chet Haase5f0d9762012-10-18 12:01:34 -0700426 c.clipRect(0, 0, mBackground.getWidth(), mBackground.getHeight(),
427 Op.DIFFERENCE);
Romain Guy407ec782011-08-24 17:06:58 -0700428 c.drawColor(0xff000000);
429 c.restore();
430 }
431 if (mBackground != null) {
432 c.drawBitmap(mBackground, 0, 0, null);
433 }
434 } finally {
435 sh.unlockCanvasAndPost(c);
436 }
437 }
438 }
439
Romain Guyf9296292011-08-25 17:00:39 -0700440 private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
441 if (!initGL(sh)) return false;
Romain Guy407ec782011-08-24 17:06:58 -0700442
Chet Haase5f0d9762012-10-18 12:01:34 -0700443 final float right = left + mBackground.getWidth();
444 final float bottom = top + mBackground.getHeight();
Romain Guy407ec782011-08-24 17:06:58 -0700445
446 final Rect frame = sh.getSurfaceFrame();
Romain Guy407ec782011-08-24 17:06:58 -0700447 final Matrix4f ortho = new Matrix4f();
448 ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
449
450 final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
451
452 final int texture = loadTexture(mBackground);
453 final int program = buildProgram(sSimpleVS, sSimpleFS);
Chet Haase2f200812012-10-11 12:49:08 -0700454
Romain Guy407ec782011-08-24 17:06:58 -0700455 final int attribPosition = glGetAttribLocation(program, "position");
456 final int attribTexCoords = glGetAttribLocation(program, "texCoords");
457 final int uniformTexture = glGetUniformLocation(program, "texture");
458 final int uniformProjection = glGetUniformLocation(program, "projection");
459
460 checkGlError();
461
462 glViewport(0, 0, frame.width(), frame.height());
463 glBindTexture(GL_TEXTURE_2D, texture);
464
465 glUseProgram(program);
466 glEnableVertexAttribArray(attribPosition);
467 glEnableVertexAttribArray(attribTexCoords);
468 glUniform1i(uniformTexture, 0);
469 glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
470
471 checkGlError();
472
473 if (w < 0 || h < 0) {
474 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
475 glClear(GL_COLOR_BUFFER_BIT);
476 }
Chet Haase2f200812012-10-11 12:49:08 -0700477
Romain Guy407ec782011-08-24 17:06:58 -0700478 // drawQuad
479 triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
480 glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
481 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
482
483 triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
484 glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
485 TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
486
487 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
Chet Haase2f200812012-10-11 12:49:08 -0700488
Romain Guyc8d983f2013-02-20 10:05:36 -0800489 boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
Romain Guy407ec782011-08-24 17:06:58 -0700490 checkEglError();
Chet Haase2f200812012-10-11 12:49:08 -0700491
Romain Guy407ec782011-08-24 17:06:58 -0700492 finishGL();
Romain Guyf9296292011-08-25 17:00:39 -0700493
Romain Guyc8d983f2013-02-20 10:05:36 -0800494 return status;
Romain Guy407ec782011-08-24 17:06:58 -0700495 }
496
497 private FloatBuffer createMesh(int left, int top, float right, float bottom) {
498 final float[] verticesData = {
499 // X, Y, Z, U, V
500 left, bottom, 0.0f, 0.0f, 1.0f,
501 right, bottom, 0.0f, 1.0f, 1.0f,
502 left, top, 0.0f, 0.0f, 0.0f,
503 right, top, 0.0f, 1.0f, 0.0f,
504 };
505
506 final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
507 final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
508 ByteOrder.nativeOrder()).asFloatBuffer();
509 triangleVertices.put(verticesData).position(0);
510 return triangleVertices;
511 }
512
513 private int loadTexture(Bitmap bitmap) {
514 int[] textures = new int[1];
John Spurlock209bede2013-07-17 12:23:27 -0400515
Romain Guy407ec782011-08-24 17:06:58 -0700516 glActiveTexture(GL_TEXTURE0);
517 glGenTextures(1, textures, 0);
518 checkGlError();
John Spurlock209bede2013-07-17 12:23:27 -0400519
Romain Guy407ec782011-08-24 17:06:58 -0700520 int texture = textures[0];
521 glBindTexture(GL_TEXTURE_2D, texture);
522 checkGlError();
John Spurlock209bede2013-07-17 12:23:27 -0400523
Romain Guy407ec782011-08-24 17:06:58 -0700524 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
525 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
John Spurlock209bede2013-07-17 12:23:27 -0400526
Romain Guy407ec782011-08-24 17:06:58 -0700527 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
528 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
John Spurlock209bede2013-07-17 12:23:27 -0400529
Romain Guy407ec782011-08-24 17:06:58 -0700530 GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
531 checkGlError();
532
Romain Guy407ec782011-08-24 17:06:58 -0700533 return texture;
534 }
John Spurlock209bede2013-07-17 12:23:27 -0400535
Romain Guy407ec782011-08-24 17:06:58 -0700536 private int buildProgram(String vertex, String fragment) {
537 int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
538 if (vertexShader == 0) return 0;
John Spurlock209bede2013-07-17 12:23:27 -0400539
Romain Guy407ec782011-08-24 17:06:58 -0700540 int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
541 if (fragmentShader == 0) return 0;
John Spurlock209bede2013-07-17 12:23:27 -0400542
Romain Guy407ec782011-08-24 17:06:58 -0700543 int program = glCreateProgram();
544 glAttachShader(program, vertexShader);
545 checkGlError();
John Spurlock209bede2013-07-17 12:23:27 -0400546
Romain Guy407ec782011-08-24 17:06:58 -0700547 glAttachShader(program, fragmentShader);
548 checkGlError();
John Spurlock209bede2013-07-17 12:23:27 -0400549
Romain Guy407ec782011-08-24 17:06:58 -0700550 glLinkProgram(program);
551 checkGlError();
John Spurlock209bede2013-07-17 12:23:27 -0400552
Romain Guy407ec782011-08-24 17:06:58 -0700553 int[] status = new int[1];
554 glGetProgramiv(program, GL_LINK_STATUS, status, 0);
555 if (status[0] != GL_TRUE) {
556 String error = glGetProgramInfoLog(program);
557 Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
558 glDeleteShader(vertexShader);
559 glDeleteShader(fragmentShader);
560 glDeleteProgram(program);
561 return 0;
562 }
John Spurlock209bede2013-07-17 12:23:27 -0400563
Romain Guy407ec782011-08-24 17:06:58 -0700564 return program;
565 }
Romain Guy3696779b2013-01-28 14:04:07 -0800566
Romain Guy407ec782011-08-24 17:06:58 -0700567 private int buildShader(String source, int type) {
568 int shader = glCreateShader(type);
John Spurlock209bede2013-07-17 12:23:27 -0400569
Romain Guy407ec782011-08-24 17:06:58 -0700570 glShaderSource(shader, source);
571 checkGlError();
John Spurlock209bede2013-07-17 12:23:27 -0400572
Romain Guy407ec782011-08-24 17:06:58 -0700573 glCompileShader(shader);
574 checkGlError();
John Spurlock209bede2013-07-17 12:23:27 -0400575
Romain Guy407ec782011-08-24 17:06:58 -0700576 int[] status = new int[1];
577 glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
578 if (status[0] != GL_TRUE) {
579 String error = glGetShaderInfoLog(shader);
580 Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
581 glDeleteShader(shader);
582 return 0;
583 }
John Spurlock209bede2013-07-17 12:23:27 -0400584
Romain Guy407ec782011-08-24 17:06:58 -0700585 return shader;
586 }
Romain Guy3696779b2013-01-28 14:04:07 -0800587
Romain Guy407ec782011-08-24 17:06:58 -0700588 private void checkEglError() {
589 int error = mEgl.eglGetError();
590 if (error != EGL_SUCCESS) {
591 Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
592 }
593 }
Romain Guy3696779b2013-01-28 14:04:07 -0800594
Romain Guy407ec782011-08-24 17:06:58 -0700595 private void checkGlError() {
596 int error = glGetError();
597 if (error != GL_NO_ERROR) {
598 Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
599 }
600 }
Romain Guy3696779b2013-01-28 14:04:07 -0800601
Romain Guy407ec782011-08-24 17:06:58 -0700602 private void finishGL() {
Romain Guyf9296292011-08-25 17:00:39 -0700603 mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
Romain Guy407ec782011-08-24 17:06:58 -0700604 mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
Romain Guyf9296292011-08-25 17:00:39 -0700605 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
Romain Guy3696779b2013-01-28 14:04:07 -0800606 mEgl.eglTerminate(mEglDisplay);
Romain Guy407ec782011-08-24 17:06:58 -0700607 }
Romain Guy3696779b2013-01-28 14:04:07 -0800608
Romain Guyf9296292011-08-25 17:00:39 -0700609 private boolean initGL(SurfaceHolder surfaceHolder) {
Romain Guy407ec782011-08-24 17:06:58 -0700610 mEgl = (EGL10) EGLContext.getEGL();
John Spurlock209bede2013-07-17 12:23:27 -0400611
Romain Guy407ec782011-08-24 17:06:58 -0700612 mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
613 if (mEglDisplay == EGL_NO_DISPLAY) {
614 throw new RuntimeException("eglGetDisplay failed " +
615 GLUtils.getEGLErrorString(mEgl.eglGetError()));
616 }
John Spurlock209bede2013-07-17 12:23:27 -0400617
Romain Guy407ec782011-08-24 17:06:58 -0700618 int[] version = new int[2];
619 if (!mEgl.eglInitialize(mEglDisplay, version)) {
620 throw new RuntimeException("eglInitialize failed " +
621 GLUtils.getEGLErrorString(mEgl.eglGetError()));
622 }
John Spurlock209bede2013-07-17 12:23:27 -0400623
Romain Guy407ec782011-08-24 17:06:58 -0700624 mEglConfig = chooseEglConfig();
625 if (mEglConfig == null) {
626 throw new RuntimeException("eglConfig not initialized");
627 }
John Spurlock209bede2013-07-17 12:23:27 -0400628
Romain Guy407ec782011-08-24 17:06:58 -0700629 mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
Chet Haase187e1e22013-03-13 18:04:00 -0700630 if (mEglContext == EGL_NO_CONTEXT) {
631 throw new RuntimeException("createContext failed " +
632 GLUtils.getEGLErrorString(mEgl.eglGetError()));
Wim Vander Schelden9549c062013-02-07 15:51:51 +0000633 }
Chet Haase187e1e22013-03-13 18:04:00 -0700634
Chet Haase2e417be2013-03-14 09:28:42 -0700635 int attribs[] = {
636 EGL_WIDTH, 1,
637 EGL_HEIGHT, 1,
638 EGL_NONE
639 };
640 EGLSurface tmpSurface = mEgl.eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
641 mEgl.eglMakeCurrent(mEglDisplay, tmpSurface, tmpSurface, mEglContext);
642
643 int[] maxSize = new int[1];
644 Rect frame = surfaceHolder.getSurfaceFrame();
645 glGetIntegerv(GL_MAX_TEXTURE_SIZE, maxSize, 0);
646
647 mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
648 mEgl.eglDestroySurface(mEglDisplay, tmpSurface);
649
650 if(frame.width() > maxSize[0] || frame.height() > maxSize[0]) {
651 mEgl.eglDestroyContext(mEglDisplay, mEglContext);
652 mEgl.eglTerminate(mEglDisplay);
653 Log.e(GL_LOG_TAG, "requested texture size " +
654 frame.width() + "x" + frame.height() + " exceeds the support maximum of " +
655 maxSize[0] + "x" + maxSize[0]);
656 return false;
657 }
658
Romain Guy407ec782011-08-24 17:06:58 -0700659 mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
Romain Guy407ec782011-08-24 17:06:58 -0700660 if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
661 int error = mEgl.eglGetError();
Wim Vander Schelden9549c062013-02-07 15:51:51 +0000662 if (error == EGL_BAD_NATIVE_WINDOW || error == EGL_BAD_ALLOC) {
663 Log.e(GL_LOG_TAG, "createWindowSurface returned " +
664 GLUtils.getEGLErrorString(error) + ".");
Romain Guyf9296292011-08-25 17:00:39 -0700665 return false;
Romain Guy407ec782011-08-24 17:06:58 -0700666 }
667 throw new RuntimeException("createWindowSurface failed " +
668 GLUtils.getEGLErrorString(error));
669 }
John Spurlock209bede2013-07-17 12:23:27 -0400670
Romain Guy407ec782011-08-24 17:06:58 -0700671 if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
672 throw new RuntimeException("eglMakeCurrent failed " +
673 GLUtils.getEGLErrorString(mEgl.eglGetError()));
674 }
Romain Guyf9296292011-08-25 17:00:39 -0700675
676 return true;
Romain Guy407ec782011-08-24 17:06:58 -0700677 }
John Spurlock209bede2013-07-17 12:23:27 -0400678
679
Romain Guy407ec782011-08-24 17:06:58 -0700680 EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
681 int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
John Spurlock209bede2013-07-17 12:23:27 -0400682 return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
Romain Guy407ec782011-08-24 17:06:58 -0700683 }
John Spurlock209bede2013-07-17 12:23:27 -0400684
Romain Guy407ec782011-08-24 17:06:58 -0700685 private EGLConfig chooseEglConfig() {
686 int[] configsCount = new int[1];
687 EGLConfig[] configs = new EGLConfig[1];
688 int[] configSpec = getConfig();
689 if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
690 throw new IllegalArgumentException("eglChooseConfig failed " +
691 GLUtils.getEGLErrorString(mEgl.eglGetError()));
692 } else if (configsCount[0] > 0) {
693 return configs[0];
694 }
695 return null;
696 }
697
698 private int[] getConfig() {
699 return new int[] {
700 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
701 EGL_RED_SIZE, 8,
702 EGL_GREEN_SIZE, 8,
703 EGL_BLUE_SIZE, 8,
704 EGL_ALPHA_SIZE, 0,
705 EGL_DEPTH_SIZE, 0,
706 EGL_STENCIL_SIZE, 0,
Romain Guy8efca542012-10-15 18:09:49 -0700707 EGL_CONFIG_CAVEAT, EGL_NONE,
Romain Guy407ec782011-08-24 17:06:58 -0700708 EGL_NONE
709 };
Romain Guyef654bd2009-08-11 19:12:17 -0700710 }
Dianne Hackborn4c62fc02009-08-08 20:40:27 -0700711 }
712}