ImageWallpaper.java revision ba39839444532af0ed3766f736582413f6d7a40b
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
17package com.android.systemui;
18
19import java.io.IOException;
20
21import android.app.WallpaperManager;
22import android.graphics.Canvas;
23import android.graphics.Rect;
24import android.graphics.Region.Op;
25import android.graphics.drawable.Drawable;
26import android.os.Handler;
27import android.service.wallpaper.WallpaperService;
28import android.util.Log;
29import android.util.Slog;
30import android.view.MotionEvent;
31import android.view.SurfaceHolder;
32import android.content.Context;
33import android.content.IntentFilter;
34import android.content.Intent;
35import android.content.BroadcastReceiver;
36
37/**
38 * Default built-in wallpaper that simply shows a static image.
39 */
40public class ImageWallpaper extends WallpaperService {
41    private static final String TAG = "ImageWallpaper";
42    private static final boolean DEBUG = false;
43
44    static final boolean FIXED_SIZED_SURFACE = true;
45
46    WallpaperManager mWallpaperManager;
47    private Handler mHandler;
48
49    @Override
50    public void onCreate() {
51        super.onCreate();
52        mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
53        mHandler = new Handler();
54    }
55
56    public Engine onCreateEngine() {
57        return new DrawableEngine();
58    }
59
60    class DrawableEngine extends Engine {
61        private final Object mLock = new Object();
62        private WallpaperObserver mReceiver;
63        Drawable mBackground;
64        int mBackgroundWidth = -1, mBackgroundHeight = -1;
65        float mXOffset;
66        float mYOffset;
67
68        boolean mVisible = true;
69        boolean mRedrawNeeded;
70        boolean mOffsetsChanged;
71        int mLastXTranslation;
72        int mLastYTranslation;
73
74        class WallpaperObserver extends BroadcastReceiver {
75            public void onReceive(Context context, Intent intent) {
76                if (DEBUG) {
77                    Log.d(TAG, "onReceive");
78                }
79
80                synchronized (mLock) {
81                    mBackgroundWidth = mBackgroundHeight = -1;
82                    mBackground = null;
83                    mRedrawNeeded = true;
84                    drawFrameLocked();
85                }
86            }
87        }
88
89        @Override
90        public void onCreate(SurfaceHolder surfaceHolder) {
91            if (DEBUG) {
92                Log.d(TAG, "onCreate");
93            }
94
95            super.onCreate(surfaceHolder);
96            IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
97            mReceiver = new WallpaperObserver();
98            registerReceiver(mReceiver, filter, null, mHandler);
99
100            updateSurfaceSize(surfaceHolder);
101        }
102
103        @Override
104        public void onDestroy() {
105            super.onDestroy();
106            unregisterReceiver(mReceiver);
107        }
108
109        @Override
110        public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
111            super.onDesiredSizeChanged(desiredWidth, desiredHeight);
112            SurfaceHolder surfaceHolder = getSurfaceHolder();
113            if (surfaceHolder != null) {
114                updateSurfaceSize(surfaceHolder);
115            }
116        }
117
118        void updateSurfaceSize(SurfaceHolder surfaceHolder) {
119            if (FIXED_SIZED_SURFACE) {
120                // Used a fixed size surface, because we are special.  We can do
121                // this because we know the current design of window animations doesn't
122                // cause this to break.
123                surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
124            } else {
125                surfaceHolder.setSizeFromLayout();
126            }
127        }
128
129        @Override
130        public void onVisibilityChanged(boolean visible) {
131            if (DEBUG) {
132                Log.d(TAG, "onVisibilityChanged: visible=" + visible);
133            }
134
135            synchronized (mLock) {
136                if (mVisible != visible) {
137                    if (DEBUG) {
138                        Log.d(TAG, "Visibility changed to visible=" + visible);
139                    }
140                    mVisible = visible;
141                    drawFrameLocked();
142                }
143            }
144        }
145
146        @Override
147        public void onTouchEvent(MotionEvent event) {
148            super.onTouchEvent(event);
149        }
150
151        @Override
152        public void onOffsetsChanged(float xOffset, float yOffset,
153                float xOffsetStep, float yOffsetStep,
154                int xPixels, int yPixels) {
155            if (DEBUG) {
156                Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
157                        + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
158                        + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
159            }
160
161            synchronized (mLock) {
162                if (mXOffset != xOffset || mYOffset != yOffset) {
163                    if (DEBUG) {
164                        Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
165                    }
166                    mXOffset = xOffset;
167                    mYOffset = yOffset;
168                    mOffsetsChanged = true;
169                }
170                drawFrameLocked();
171            }
172        }
173
174        @Override
175        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
176            if (DEBUG) {
177                Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
178            }
179
180            super.onSurfaceChanged(holder, format, width, height);
181
182            synchronized (mLock) {
183                mRedrawNeeded = true;
184                drawFrameLocked();
185            }
186        }
187
188        void drawFrameLocked() {
189            if (!mVisible) {
190                if (DEBUG) {
191                    Log.d(TAG, "Suppressed drawFrame since wallpaper is not visible.");
192                }
193                return;
194            }
195            if (!mRedrawNeeded && !mOffsetsChanged) {
196                if (DEBUG) {
197                    Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
198                            + "and offsets have not changed.");
199                }
200                return;
201            }
202
203            if (mBackgroundWidth < 0 || mBackgroundHeight < 0) {
204                // If we don't yet know the size of the wallpaper bitmap,
205                // we need to get it now.
206                updateWallpaperLocked();
207            }
208
209            SurfaceHolder sh = getSurfaceHolder();
210            final Rect frame = sh.getSurfaceFrame();
211            final int dw = frame.width();
212            final int dh = frame.height();
213            final int availw = dw - mBackgroundWidth;
214            final int availh = dh - mBackgroundHeight;
215            int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
216            int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
217
218            mOffsetsChanged = false;
219            if (!mRedrawNeeded
220                    && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
221                if (DEBUG) {
222                    Log.d(TAG, "Suppressed drawFrame since the image has not "
223                            + "actually moved an integral number of pixels.");
224                }
225                return;
226            }
227            mRedrawNeeded = false;
228            mLastXTranslation = xPixels;
229            mLastYTranslation = yPixels;
230
231            if (mBackground == null) {
232                // If we somehow got to this point after we have last flushed
233                // the wallpaper, well we really need it to draw again.  So
234                // seems like we need to reload it.  Ouch.
235                updateWallpaperLocked();
236            }
237
238            //Slog.i(TAG, "************** DRAWING WALLAPER ******************");
239            Canvas c = sh.lockCanvas();
240            if (c != null) {
241                try {
242                    if (DEBUG) {
243                        Log.d(TAG, "Redrawing: xPixels=" + xPixels + ", yPixels=" + yPixels);
244                    }
245
246                    c.translate(xPixels, yPixels);
247                    if (availw < 0 || availh < 0) {
248                        c.save(Canvas.CLIP_SAVE_FLAG);
249                        c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE);
250                        c.drawColor(0xff000000);
251                        c.restore();
252                    }
253                    if (mBackground != null) {
254                        mBackground.draw(c);
255                    }
256                } finally {
257                    sh.unlockCanvasAndPost(c);
258                }
259            }
260
261            if (FIXED_SIZED_SURFACE) {
262                // If the surface is fixed-size, we should only need to
263                // draw it once and then we'll let the window manager
264                // position it appropriately.  As such, we no longer needed
265                // the loaded bitmap.  Yay!
266                mBackground = null;
267                mWallpaperManager.forgetLoadedWallpaper();
268            }
269        }
270
271        void updateWallpaperLocked() {
272            //Slog.i(TAG, "************** LOADING WALLAPER ******************");
273            Throwable exception = null;
274            try {
275                mBackground = mWallpaperManager.getFastDrawable();
276            } catch (RuntimeException e) {
277                exception = e;
278            } catch (OutOfMemoryError e) {
279                exception = e;
280            }
281            if (exception != null) {
282                mBackground = null;
283                // Note that if we do fail at this, and the default wallpaper can't
284                // be loaded, we will go into a cycle.  Don't do a build where the
285                // default wallpaper can't be loaded.
286                Log.w(TAG, "Unable to load wallpaper!", exception);
287                try {
288                    mWallpaperManager.clear();
289                } catch (IOException ex) {
290                    // now we're really screwed.
291                    Log.w(TAG, "Unable reset to default wallpaper!", ex);
292                }
293            }
294            mBackgroundWidth = mBackground != null ? mBackground.getIntrinsicWidth() : 0;
295            mBackgroundHeight = mBackground != null ? mBackground.getIntrinsicHeight() : 0;
296        }
297    }
298}
299