14c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn/*
24c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * Copyright (C) 2009 The Android Open Source Project
34c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn *
44c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License");
54c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * you may not use this file except in compliance with the License.
64c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * You may obtain a copy of the License at
74c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn *
84c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn *      http://www.apache.org/licenses/LICENSE-2.0
94c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn *
104c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * Unless required by applicable law or agreed to in writing, software
114c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS,
124c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * See the License for the specific language governing permissions and
144c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * limitations under the License.
154c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn */
164c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn
17ba39839444532af0ed3766f736582413f6d7a40bDianne Hackbornpackage com.android.systemui;
184c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn
19407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.app.ActivityManager;
204c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackbornimport android.app.WallpaperManager;
21407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.content.BroadcastReceiver;
22407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.content.Context;
23407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.content.Intent;
24407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.graphics.Bitmap;
254c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackbornimport android.graphics.Canvas;
26759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackbornimport android.graphics.Rect;
27e2d034c9014919a45ddd717d4e564e73771b2fefMathias Agopianimport android.graphics.Region.Op;
28407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.opengl.GLUtils;
29043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guyimport android.os.SystemProperties;
30407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.renderscript.Matrix4f;
314c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackbornimport android.service.wallpaper.WallpaperService;
32c9dbbe28f7879bd377114587ed1f40235a2d37caDianne Hackbornimport android.util.Log;
33407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.view.Display;
348df8b2b405c60cacf7a66c4e2ca078dd3d7ec7bdDianne Hackbornimport android.view.MotionEvent;
354c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackbornimport android.view.SurfaceHolder;
36407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport android.view.WindowManager;
37407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
38407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport javax.microedition.khronos.egl.EGL10;
39407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport javax.microedition.khronos.egl.EGLConfig;
40407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport javax.microedition.khronos.egl.EGLContext;
41407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport javax.microedition.khronos.egl.EGLDisplay;
42407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport javax.microedition.khronos.egl.EGLSurface;
43407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport javax.microedition.khronos.opengles.GL;
44407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport java.io.IOException;
45407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport java.nio.ByteBuffer;
46407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport java.nio.ByteOrder;
47407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport java.nio.FloatBuffer;
48407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
49407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport static android.opengl.GLES20.*;
50407ec78b828173257b0c5dae221649a4ccd8b058Romain Guyimport static javax.microedition.khronos.egl.EGL10.*;
514c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn
524c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn/**
534c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn * Default built-in wallpaper that simply shows a static image.
544c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn */
55407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy@SuppressWarnings({"UnusedDeclaration"})
564c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackbornpublic class ImageWallpaper extends WallpaperService {
57fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown    private static final String TAG = "ImageWallpaper";
58407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy    private static final String GL_LOG_TAG = "ImageWallpaperGL";
59fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown    private static final boolean DEBUG = false;
60043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy    private static final String PROPERTY_KERNEL_QEMU = "ro.kernel.qemu";
61fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown
62ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn    static final boolean FIXED_SIZED_SURFACE = true;
63881fb2092b41f4447e708da2f341d2ca5602c0d4Erik Gilling    static final boolean USE_OPENGL = true;
64ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn
65ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy    WallpaperManager mWallpaperManager;
66407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
67407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy    boolean mIsHwAccelerated;
68ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy
69ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy    @Override
70ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy    public void onCreate() {
71ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy        super.onCreate();
72ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy        mWallpaperManager = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
73407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
74407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        //noinspection PointlessBooleanExpression,ConstantConditions
75407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        if (FIXED_SIZED_SURFACE && USE_OPENGL) {
76043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy            if (!isEmulator()) {
77043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy                WindowManager windowManager =
78043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy                        (WindowManager) getSystemService(Context.WINDOW_SERVICE);
79043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy                Display display = windowManager.getDefaultDisplay();
80043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy                mIsHwAccelerated = ActivityManager.isHighEndGfx(display);
81043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy            }
82407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
83ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy    }
84ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy
85043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy    private static boolean isEmulator() {
86043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy        return "1".equals(SystemProperties.get(PROPERTY_KERNEL_QEMU, "0"));
87043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy    }
88043a6b1e5709c46cb8094766c792ec57d3fd97dfRomain Guy
89ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy    public Engine onCreateEngine() {
90284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn        return new DrawableEngine();
91ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy    }
92ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy
93ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy    class DrawableEngine extends Engine {
94407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
95407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        static final int EGL_OPENGL_ES2_BIT = 4;
96407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
97407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private final Object mLock = new Object[0];
98407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
99407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        // TODO: Not currently used, keeping around until we know we don't need it
100407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        @SuppressWarnings({"UnusedDeclaration"})
101284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn        private WallpaperObserver mReceiver;
102407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
103407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        Bitmap mBackground;
104ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn        int mBackgroundWidth = -1, mBackgroundHeight = -1;
1057341d7a104b47996445d069a695e155a07184606Dianne Hackborn        float mXOffset;
1067341d7a104b47996445d069a695e155a07184606Dianne Hackborn        float mYOffset;
107759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn
108fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown        boolean mVisible = true;
109fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown        boolean mRedrawNeeded;
110fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown        boolean mOffsetsChanged;
111fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown        int mLastXTranslation;
112fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown        int mLastYTranslation;
113fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown
114407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private EGL10 mEgl;
115407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private EGLDisplay mEglDisplay;
116407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private EGLConfig mEglConfig;
117407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private EGLContext mEglContext;
118407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private EGLSurface mEglSurface;
119407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private GL mGL;
120407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
121407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private static final String sSimpleVS =
122407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "attribute vec4 position;\n" +
123407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "attribute vec2 texCoords;\n" +
124407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "varying vec2 outTexCoords;\n" +
125407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "uniform mat4 projection;\n" +
126407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "\nvoid main(void) {\n" +
127407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "    outTexCoords = texCoords;\n" +
128407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "    gl_Position = projection * position;\n" +
129407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "}\n\n";
130407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private static final String sSimpleFS =
131407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "precision mediump float;\n\n" +
132407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "varying vec2 outTexCoords;\n" +
133407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "uniform sampler2D texture;\n" +
134407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "\nvoid main(void) {\n" +
135407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
136407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                "}\n\n";
137407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
138407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private static final int FLOAT_SIZE_BYTES = 4;
139407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
140407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
141407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private static final int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;
142407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
143284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn        class WallpaperObserver extends BroadcastReceiver {
144284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn            public void onReceive(Context context, Intent intent) {
14530bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                if (DEBUG) {
14630bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                    Log.d(TAG, "onReceive");
14730bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                }
14830bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown
149fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                synchronized (mLock) {
150ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                    mBackgroundWidth = mBackgroundHeight = -1;
151ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                    mBackground = null;
152ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                    mRedrawNeeded = true;
15330bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                    drawFrameLocked();
154fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                }
155284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn            }
156284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn        }
157284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn
15835be7560300a97fc3675bdd325910f28827d9508Jeff Sharkey        public DrawableEngine() {
15935be7560300a97fc3675bdd325910f28827d9508Jeff Sharkey            super();
16035be7560300a97fc3675bdd325910f28827d9508Jeff Sharkey            setFixedSizeAllowed(true);
16135be7560300a97fc3675bdd325910f28827d9508Jeff Sharkey        }
16235be7560300a97fc3675bdd325910f28827d9508Jeff Sharkey
1634c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn        @Override
164759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn        public void onCreate(SurfaceHolder surfaceHolder) {
16530bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            if (DEBUG) {
16630bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                Log.d(TAG, "onCreate");
16730bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            }
16830bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown
169759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn            super.onCreate(surfaceHolder);
1709ea31639738e8d2c90dc3a4fcd535d09a0b7209aDianne Hackborn
171407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            // TODO: Don't need this currently because the wallpaper service
1729ea31639738e8d2c90dc3a4fcd535d09a0b7209aDianne Hackborn            // will restart the image wallpaper whenever the image changes.
1739ea31639738e8d2c90dc3a4fcd535d09a0b7209aDianne Hackborn            //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
1749ea31639738e8d2c90dc3a4fcd535d09a0b7209aDianne Hackborn            //mReceiver = new WallpaperObserver();
1759ea31639738e8d2c90dc3a4fcd535d09a0b7209aDianne Hackborn            //registerReceiver(mReceiver, filter, null, mHandler);
176fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown
177ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn            updateSurfaceSize(surfaceHolder);
178a8e5a2bcd6a0d35893187c6df42425c03be005daChet Haase
179a8e5a2bcd6a0d35893187c6df42425c03be005daChet Haase            setOffsetNotificationsEnabled(false);
1804c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn        }
1814c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn
1824c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn        @Override
183284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn        public void onDestroy() {
184284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn            super.onDestroy();
1859ea31639738e8d2c90dc3a4fcd535d09a0b7209aDianne Hackborn            if (mReceiver != null) {
1869ea31639738e8d2c90dc3a4fcd535d09a0b7209aDianne Hackborn                unregisterReceiver(mReceiver);
1879ea31639738e8d2c90dc3a4fcd535d09a0b7209aDianne Hackborn            }
188284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn        }
189284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn
190284ac93aa30642fda87d5c40263a1263677c21cdDianne Hackborn        @Override
191ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn        public void onDesiredSizeChanged(int desiredWidth, int desiredHeight) {
192912d9d13698693709991b76f83a8d64179e97e83Dianne Hackborn            super.onDesiredSizeChanged(desiredWidth, desiredHeight);
193ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn            SurfaceHolder surfaceHolder = getSurfaceHolder();
194ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn            if (surfaceHolder != null) {
195ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn                updateSurfaceSize(surfaceHolder);
196ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn            }
197ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn        }
198ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn
199ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn        void updateSurfaceSize(SurfaceHolder surfaceHolder) {
200ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn            if (FIXED_SIZED_SURFACE) {
201ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                // Used a fixed size surface, because we are special.  We can do
202ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                // this because we know the current design of window animations doesn't
203ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                // cause this to break.
204ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                surfaceHolder.setFixedSize(getDesiredMinimumWidth(), getDesiredMinimumHeight());
205ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn            } else {
206ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                surfaceHolder.setSizeFromLayout();
207ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn            }
208ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn        }
209ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn
210ac1471a4fff660710f88afc679c4119fdf8dc417Dianne Hackborn        @Override
211759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn        public void onVisibilityChanged(boolean visible) {
21230bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            if (DEBUG) {
21330bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                Log.d(TAG, "onVisibilityChanged: visible=" + visible);
21430bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            }
21530bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown
216fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            synchronized (mLock) {
21730bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                if (mVisible != visible) {
21830bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                    if (DEBUG) {
21930bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                        Log.d(TAG, "Visibility changed to visible=" + visible);
22030bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                    }
22130bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                    mVisible = visible;
22230bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                    drawFrameLocked();
22330bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                }
224fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            }
225759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn        }
226fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown
227759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn        @Override
2288df8b2b405c60cacf7a66c4e2ca078dd3d7ec7bdDianne Hackborn        public void onTouchEvent(MotionEvent event) {
2298df8b2b405c60cacf7a66c4e2ca078dd3d7ec7bdDianne Hackborn            super.onTouchEvent(event);
2308df8b2b405c60cacf7a66c4e2ca078dd3d7ec7bdDianne Hackborn        }
2318df8b2b405c60cacf7a66c4e2ca078dd3d7ec7bdDianne Hackborn
2328df8b2b405c60cacf7a66c4e2ca078dd3d7ec7bdDianne Hackborn        @Override
23372c82ab9923025a91bbabb32e56bfea27bfd083bDianne Hackborn        public void onOffsetsChanged(float xOffset, float yOffset,
234bf6956b1d95442e9d9c483894d578fe6b7044cbbMarco Nelissen                float xOffsetStep, float yOffsetStep,
23572c82ab9923025a91bbabb32e56bfea27bfd083bDianne Hackborn                int xPixels, int yPixels) {
23630bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            if (DEBUG) {
23730bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                Log.d(TAG, "onOffsetsChanged: xOffset=" + xOffset + ", yOffset=" + yOffset
23830bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                        + ", xOffsetStep=" + xOffsetStep + ", yOffsetStep=" + yOffsetStep
23930bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                        + ", xPixels=" + xPixels + ", yPixels=" + yPixels);
24030bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            }
24130bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown
242fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            synchronized (mLock) {
243fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                if (mXOffset != xOffset || mYOffset != yOffset) {
244fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    if (DEBUG) {
245fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                        Log.d(TAG, "Offsets changed to (" + xOffset + "," + yOffset + ").");
246fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    }
247fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    mXOffset = xOffset;
248fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    mYOffset = yOffset;
24930bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                    mOffsetsChanged = true;
250fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                }
25130bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                drawFrameLocked();
252fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            }
25372c82ab9923025a91bbabb32e56bfea27bfd083bDianne Hackborn        }
25472c82ab9923025a91bbabb32e56bfea27bfd083bDianne Hackborn
25572c82ab9923025a91bbabb32e56bfea27bfd083bDianne Hackborn        @Override
2564c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
25730bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            if (DEBUG) {
25830bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
259fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            }
2604c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn
26130bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            super.onSurfaceChanged(holder, format, width, height);
2624c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn
26330bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            synchronized (mLock) {
26430bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                mRedrawNeeded = true;
26530bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown                drawFrameLocked();
26630bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown            }
2674c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn        }
268fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown
26930bc34f191ca8a009af313fc751e5b4bff6e39a1Jeff Brown        void drawFrameLocked() {
270fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            if (!mVisible) {
271fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                if (DEBUG) {
272fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    Log.d(TAG, "Suppressed drawFrame since wallpaper is not visible.");
273fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                }
274fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                return;
275fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            }
276fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            if (!mRedrawNeeded && !mOffsetsChanged) {
277fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                if (DEBUG) {
278fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    Log.d(TAG, "Suppressed drawFrame since redraw is not needed "
279fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                            + "and offsets have not changed.");
280fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                }
281fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                return;
282fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            }
283fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown
284321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka            // If we don't yet know the size of the wallpaper bitmap,
285321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka            // we need to get it now.
286321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka            boolean updateWallpaper = mBackgroundWidth < 0 || mBackgroundHeight < 0 ;
287321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka
288321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka            // If we somehow got to this point after we have last flushed
289321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka            // the wallpaper, well we really need it to draw again.  So
290321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka            // seems like we need to reload it.  Ouch.
291321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka            updateWallpaper = updateWallpaper || mBackground == null;
292321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka
293321357b6066a34cc12a0528b7b835c7664db2e08Michael Jurka            if (updateWallpaper) {
294ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                updateWallpaperLocked();
295ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn            }
296ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn
297759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn            SurfaceHolder sh = getSurfaceHolder();
298033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            final Rect frame = sh.getSurfaceFrame();
299033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            final int dw = frame.width();
300033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            final int dh = frame.height();
301ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn            final int availw = dw - mBackgroundWidth;
302ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn            final int availh = dh - mBackgroundHeight;
303033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            int xPixels = availw < 0 ? (int)(availw * mXOffset + .5f) : (availw / 2);
304033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            int yPixels = availh < 0 ? (int)(availh * mYOffset + .5f) : (availh / 2);
305033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown
306033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            mOffsetsChanged = false;
307407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (!mRedrawNeeded && xPixels == mLastXTranslation && yPixels == mLastYTranslation) {
308033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown                if (DEBUG) {
309033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown                    Log.d(TAG, "Suppressed drawFrame since the image has not "
310033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown                            + "actually moved an integral number of pixels.");
311033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown                }
312033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown                return;
313033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            }
314033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            mRedrawNeeded = false;
315033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            mLastXTranslation = xPixels;
316033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown            mLastYTranslation = yPixels;
317033f63a1a5e8f768a72a11561fe70957eb44fa3eJeff Brown
318ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn
319407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (mIsHwAccelerated) {
320f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy                if (!drawWallpaperWithOpenGL(sh, availw, availh, xPixels, yPixels)) {
321f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy                    drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
322f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy                }
323407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            } else {
324407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                drawWallpaperWithCanvas(sh, availw, availh, xPixels, yPixels);
325759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn            }
326ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn
327ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn            if (FIXED_SIZED_SURFACE) {
328ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                // If the surface is fixed-size, we should only need to
329ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                // draw it once and then we'll let the window manager
330ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                // position it appropriately.  As such, we no longer needed
331ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                // the loaded bitmap.  Yay!
332ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                mBackground = null;
333ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn                mWallpaperManager.forgetLoadedWallpaper();
334ba39839444532af0ed3766f736582413f6d7a40bDianne Hackborn            }
335759a39e8d2a8b27ef07e102394629dce68aa186bDianne Hackborn        }
336ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy
337fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown        void updateWallpaperLocked() {
338fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            Throwable exception = null;
339fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            try {
340407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                mBackground = mWallpaperManager.getBitmap();
341fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            } catch (RuntimeException e) {
342fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                exception = e;
343fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            } catch (OutOfMemoryError e) {
344fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                exception = e;
345fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            }
346407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
347fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown            if (exception != null) {
348fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                mBackground = null;
349fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                // Note that if we do fail at this, and the default wallpaper can't
350fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                // be loaded, we will go into a cycle.  Don't do a build where the
351fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                // default wallpaper can't be loaded.
352fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                Log.w(TAG, "Unable to load wallpaper!", exception);
353c9dbbe28f7879bd377114587ed1f40235a2d37caDianne Hackborn                try {
354fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    mWallpaperManager.clear();
355fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                } catch (IOException ex) {
356fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    // now we're really screwed.
357fa2e504087362989e5dd7fe6e65b6481cef59495Jeff Brown                    Log.w(TAG, "Unable reset to default wallpaper!", ex);
358c9dbbe28f7879bd377114587ed1f40235a2d37caDianne Hackborn                }
359ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy            }
360407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
361407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mBackgroundWidth = mBackground != null ? mBackground.getWidth() : 0;
362407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mBackgroundHeight = mBackground != null ? mBackground.getHeight() : 0;
363407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
364407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
365407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private void drawWallpaperWithCanvas(SurfaceHolder sh, int w, int h, int x, int y) {
366407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            Canvas c = sh.lockCanvas();
367407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (c != null) {
368407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                try {
369407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    if (DEBUG) {
370407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        Log.d(TAG, "Redrawing: x=" + x + ", y=" + y);
371407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    }
372407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
373407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    c.translate(x, y);
374407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    if (w < 0 || h < 0) {
375407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        c.save(Canvas.CLIP_SAVE_FLAG);
376407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        c.clipRect(0, 0, mBackgroundWidth, mBackgroundHeight, Op.DIFFERENCE);
377407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        c.drawColor(0xff000000);
378407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        c.restore();
379407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    }
380407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    if (mBackground != null) {
381407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        c.drawBitmap(mBackground, 0, 0, null);
382407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    }
383407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                } finally {
384407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    sh.unlockCanvasAndPost(c);
385407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                }
386407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
387407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
388407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
389f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy        private boolean drawWallpaperWithOpenGL(SurfaceHolder sh, int w, int h, int left, int top) {
390f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy            if (!initGL(sh)) return false;
391407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
392407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final float right = left + mBackgroundWidth;
393407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final float bottom = top + mBackgroundHeight;
394407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
395407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final Rect frame = sh.getSurfaceFrame();
396407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
397407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final Matrix4f ortho = new Matrix4f();
398407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            ortho.loadOrtho(0.0f, frame.width(), frame.height(), 0.0f, -1.0f, 1.0f);
399407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
400407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final FloatBuffer triangleVertices = createMesh(left, top, right, bottom);
401407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
402407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final int texture = loadTexture(mBackground);
403407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final int program = buildProgram(sSimpleVS, sSimpleFS);
404407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
405407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final int attribPosition = glGetAttribLocation(program, "position");
406407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final int attribTexCoords = glGetAttribLocation(program, "texCoords");
407407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final int uniformTexture = glGetUniformLocation(program, "texture");
408407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final int uniformProjection = glGetUniformLocation(program, "projection");
409407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
410407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
411407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
412407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glViewport(0, 0, frame.width(), frame.height());
413407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glBindTexture(GL_TEXTURE_2D, texture);
414407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
415407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glUseProgram(program);
416407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glEnableVertexAttribArray(attribPosition);
417407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glEnableVertexAttribArray(attribTexCoords);
418407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glUniform1i(uniformTexture, 0);
419407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glUniformMatrix4fv(uniformProjection, 1, false, ortho.getArray(), 0);
420407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
421407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
422407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
423407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (w < 0 || h < 0) {
424407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
425407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                glClear(GL_COLOR_BUFFER_BIT);
426407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
427407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
428407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            // drawQuad
429407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            triangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
430407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glVertexAttribPointer(attribPosition, 3, GL_FLOAT, false,
431407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
432407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
433407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            triangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
434407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glVertexAttribPointer(attribTexCoords, 3, GL_FLOAT, false,
435407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    TRIANGLE_VERTICES_DATA_STRIDE_BYTES, triangleVertices);
436407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
437407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
438407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
439407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
440407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                throw new RuntimeException("Cannot swap buffers");
441407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
442407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkEglError();
443407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
444407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            finishGL();
445f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy
446f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy            return true;
447407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
448407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
449407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private FloatBuffer createMesh(int left, int top, float right, float bottom) {
450407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final float[] verticesData = {
451407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    // X, Y, Z, U, V
452407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                     left,  bottom, 0.0f, 0.0f, 1.0f,
453407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                     right, bottom, 0.0f, 1.0f, 1.0f,
454407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                     left,  top,    0.0f, 0.0f, 0.0f,
455407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                     right, top,    0.0f, 1.0f, 0.0f,
456407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            };
457407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
458407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final int bytes = verticesData.length * FLOAT_SIZE_BYTES;
459407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            final FloatBuffer triangleVertices = ByteBuffer.allocateDirect(bytes).order(
460407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    ByteOrder.nativeOrder()).asFloatBuffer();
461407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            triangleVertices.put(verticesData).position(0);
462407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            return triangleVertices;
463407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
464407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
465407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private int loadTexture(Bitmap bitmap) {
466407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int[] textures = new int[1];
467407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
468407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glActiveTexture(GL_TEXTURE0);
469407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glGenTextures(1, textures, 0);
470407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
471407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
472407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int texture = textures[0];
473407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glBindTexture(GL_TEXTURE_2D, texture);
474407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
475407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
476407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
477407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
478407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
479407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
480407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
481407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
482407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
483407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
484407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
485407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            bitmap.recycle();
486407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
487407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            return texture;
488407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
489407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
490407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private int buildProgram(String vertex, String fragment) {
491407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
492407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (vertexShader == 0) return 0;
493407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
494407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
495407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (fragmentShader == 0) return 0;
496407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
497407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int program = glCreateProgram();
498407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glAttachShader(program, vertexShader);
499407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
500407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
501407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glAttachShader(program, fragmentShader);
502407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
503407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
504407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glLinkProgram(program);
505407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
506407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
507407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int[] status = new int[1];
508407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glGetProgramiv(program, GL_LINK_STATUS, status, 0);
509407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (status[0] != GL_TRUE) {
510407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                String error = glGetProgramInfoLog(program);
511407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                Log.d(GL_LOG_TAG, "Error while linking program:\n" + error);
512407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                glDeleteShader(vertexShader);
513407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                glDeleteShader(fragmentShader);
514407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                glDeleteProgram(program);
515407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                return 0;
516407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
517407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
518407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            return program;
519407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
520407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
521407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private int buildShader(String source, int type) {
522407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int shader = glCreateShader(type);
523407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
524407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glShaderSource(shader, source);
525407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
526407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
527407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glCompileShader(shader);
528407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            checkGlError();
529407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
530407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int[] status = new int[1];
531407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
532407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (status[0] != GL_TRUE) {
533407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                String error = glGetShaderInfoLog(shader);
534407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                Log.d(GL_LOG_TAG, "Error while compiling shader:\n" + error);
535407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                glDeleteShader(shader);
536407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                return 0;
537407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
538407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
539407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            return shader;
540407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
541407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
542407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private void checkEglError() {
543407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int error = mEgl.eglGetError();
544407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (error != EGL_SUCCESS) {
545407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                Log.w(GL_LOG_TAG, "EGL error = " + GLUtils.getEGLErrorString(error));
546407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
547407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
548407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
549407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private void checkGlError() {
550407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int error = glGetError();
551407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (error != GL_NO_ERROR) {
552407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                Log.w(GL_LOG_TAG, "GL error = 0x" + Integer.toHexString(error), new Throwable());
553407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
554407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
555407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
556407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private void finishGL() {
557f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy            mEgl.eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
558407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
559f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
560407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
561407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
562f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy        private boolean initGL(SurfaceHolder surfaceHolder) {
563407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mEgl = (EGL10) EGLContext.getEGL();
564407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
565407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
566407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (mEglDisplay == EGL_NO_DISPLAY) {
567407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                throw new RuntimeException("eglGetDisplay failed " +
568407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
569407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
570407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
571407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int[] version = new int[2];
572407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (!mEgl.eglInitialize(mEglDisplay, version)) {
573407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                throw new RuntimeException("eglInitialize failed " +
574407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
575407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
576407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
577407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mEglConfig = chooseEglConfig();
578407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (mEglConfig == null) {
579407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                throw new RuntimeException("eglConfig not initialized");
580407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
581407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
582407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
583407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
584407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null);
585407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
586407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
587407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                int error = mEgl.eglGetError();
588407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                if (error == EGL_BAD_NATIVE_WINDOW) {
589407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    Log.e(GL_LOG_TAG, "createWindowSurface returned EGL_BAD_NATIVE_WINDOW.");
590f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy                    return false;
591407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                }
592407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                throw new RuntimeException("createWindowSurface failed " +
593407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        GLUtils.getEGLErrorString(error));
594407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
595407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
596407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
597407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                throw new RuntimeException("eglMakeCurrent failed " +
598407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
599407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
600407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
601407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            mGL = mEglContext.getGL();
602f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy
603f929629e74fe84b986f76db448b9c95d72b2903eRomain Guy            return true;
604407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
605407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
606407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
607407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
608407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
609407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
610407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
611407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
612407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private EGLConfig chooseEglConfig() {
613407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int[] configsCount = new int[1];
614407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            EGLConfig[] configs = new EGLConfig[1];
615407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            int[] configSpec = getConfig();
616407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            if (!mEgl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, configsCount)) {
617407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                throw new IllegalArgumentException("eglChooseConfig failed " +
618407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                        GLUtils.getEGLErrorString(mEgl.eglGetError()));
619407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            } else if (configsCount[0] > 0) {
620407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                return configs[0];
621407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            }
622407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            return null;
623407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        }
624407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy
625407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy        private int[] getConfig() {
626407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            return new int[] {
627407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
628407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    EGL_RED_SIZE, 8,
629407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    EGL_GREEN_SIZE, 8,
630407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    EGL_BLUE_SIZE, 8,
631407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    EGL_ALPHA_SIZE, 0,
632407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    EGL_DEPTH_SIZE, 0,
633407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    EGL_STENCIL_SIZE, 0,
634407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy                    EGL_NONE
635407ec78b828173257b0c5dae221649a4ccd8b058Romain Guy            };
636ef654bdd5bd957574abd4194d7b0585f3fc3fd34Romain Guy        }
6374c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn    }
6384c62fc0e1e5ea9c69a12a7d1cf8b3ec8b2d114a3Dianne Hackborn}
639