summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ahan Wu <ahanwu@google.com> 2019-01-14 20:38:14 +0800
committer Ahan Wu <ahanwu@google.com> 2019-02-16 16:19:33 +0800
commit67e7f1054fd313e7298765d63efbdcf28aaffc87 (patch)
tree91e4bd5faf0577631908210d99e67c3fc6888fcd
parentcb889759ae1654c2b5730a72398db812df57d678 (diff)
Render ImageWallpaper with OpenGL ES and apply visual effects. (Fix bug)
We have to render image wallpaper with OpenGL ES to apply some amazing visual effects. Bug: 122803209 Bug: 124073420 Bug: 123616712 Bug: 123615467 Test: Manually. Change-Id: I0123d4ba2acb5a84b709c0468910e006c8e49563
-rw-r--r--packages/SystemUI/AndroidManifest.xml5
-rw-r--r--packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl27
-rw-r--r--packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl8
-rw-r--r--packages/SystemUI/src/com/android/systemui/ImageWallpaper.java106
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java115
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java259
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java144
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java97
-rw-r--r--packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java150
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java2
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java7
12 files changed, 914 insertions, 9 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 4a5388be2c1e..265d464d9c71 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -22,6 +22,11 @@
android:sharedUserId="android.uid.systemui"
coreApp="true">
+ <!-- Using OpenGL ES 2.0 -->
+ <uses-feature
+ android:glEsVersion="0x00020000"
+ android:required="true" />
+
<!-- SysUI must be the one to define this permission; its name is
referenced by the core OS. -->
<permission android:name="android.permission.systemui.IDENTITY"
diff --git a/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
new file mode 100644
index 000000000000..586cdf3fbaae
--- /dev/null
+++ b/packages/SystemUI/res/raw/image_wallpaper_fragment_shader.glsl
@@ -0,0 +1,27 @@
+precision mediump float;
+
+uniform sampler2D uTexture;
+uniform float uCenterReveal;
+uniform float uReveal;
+uniform float uAod2Opacity;
+varying vec2 vTextureCoordinates;
+
+vec3 luminosity(vec3 color) {
+ float lum = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
+ return vec3(lum);
+}
+
+vec4 transform(vec3 diffuse) {
+ // TODO: Add well comments here, tracking on b/123615467.
+ vec3 lum = luminosity(diffuse);
+ diffuse = mix(diffuse, lum, smoothstep(0., uCenterReveal, uReveal));
+ float val = mix(uReveal, uCenterReveal, step(uCenterReveal, uReveal));
+ diffuse = smoothstep(val, 1.0, diffuse);
+ diffuse *= uAod2Opacity * (1. - smoothstep(uCenterReveal, 1., uReveal));
+ return vec4(diffuse.r, diffuse.g, diffuse.b, 1.);
+}
+
+void main() {
+ vec4 fragColor = texture2D(uTexture, vTextureCoordinates);
+ gl_FragColor = transform(fragColor.rgb);
+} \ No newline at end of file
diff --git a/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl
new file mode 100644
index 000000000000..4393e2bb0ebf
--- /dev/null
+++ b/packages/SystemUI/res/raw/image_wallpaper_vertex_shader.glsl
@@ -0,0 +1,8 @@
+attribute vec4 aPosition;
+attribute vec2 aTextureCoordinates;
+varying vec2 vTextureCoordinates;
+
+void main() {
+ vTextureCoordinates = aTextureCoordinates;
+ gl_Position = aPosition;
+} \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 2aecc24e83c0..7e645ab77a63 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -28,7 +28,9 @@ import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region.Op;
import android.hardware.display.DisplayManager;
+import android.opengl.GLSurfaceView;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Handler;
import android.os.Trace;
import android.service.wallpaper.WallpaperService;
@@ -39,6 +41,7 @@ import android.view.Surface;
import android.view.SurfaceHolder;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.glwallpaper.ImageWallpaperRenderer;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -73,10 +76,78 @@ public class ImageWallpaper extends WallpaperService {
@Override
public Engine onCreateEngine() {
- mEngine = new DrawableEngine();
- return mEngine;
+ if (Build.IS_DEBUGGABLE) {
+ Log.v(TAG, "We are using GLEngine");
+ }
+ return new GLEngine(this);
+ }
+
+ class GLEngine extends Engine {
+ private GLWallpaperSurfaceView mWallpaperSurfaceView;
+
+ GLEngine(Context context) {
+ mWallpaperSurfaceView = new GLWallpaperSurfaceView(context);
+ mWallpaperSurfaceView.setRenderer(
+ new ImageWallpaperRenderer(context, mWallpaperSurfaceView));
+ mWallpaperSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
+ setOffsetNotificationsEnabled(true);
+ }
+
+ @Override
+ public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
+ if (mWallpaperSurfaceView != null) {
+ mWallpaperSurfaceView.notifyAmbientModeChanged(inAmbientMode, animationDuration);
+ }
+ }
+
+ @Override
+ public void onOffsetsChanged(float xOffset, float yOffset, float xOffsetStep,
+ float yOffsetStep, int xPixelOffset, int yPixelOffset) {
+ if (mWallpaperSurfaceView != null) {
+ mWallpaperSurfaceView.notifyOffsetsChanged(xOffset, yOffset);
+ }
+ }
+
+ private class GLWallpaperSurfaceView extends GLSurfaceView implements ImageGLView {
+ private WallpaperStatusListener mWallpaperChangedListener;
+
+ GLWallpaperSurfaceView(Context context) {
+ super(context);
+ setEGLContextClientVersion(2);
+ }
+
+ @Override
+ public SurfaceHolder getHolder() {
+ return getSurfaceHolder();
+ }
+
+ @Override
+ public void setRenderer(Renderer renderer) {
+ super.setRenderer(renderer);
+ mWallpaperChangedListener = (WallpaperStatusListener) renderer;
+ }
+
+ private void notifyAmbientModeChanged(boolean inAmbient, long duration) {
+ if (mWallpaperChangedListener != null) {
+ mWallpaperChangedListener.onAmbientModeChanged(inAmbient, duration);
+ }
+ }
+
+ private void notifyOffsetsChanged(float xOffset, float yOffset) {
+ if (mWallpaperChangedListener != null) {
+ mWallpaperChangedListener.onOffsetsChanged(
+ xOffset, yOffset, getHolder().getSurfaceFrame());
+ }
+ }
+
+ @Override
+ public void render() {
+ requestRender();
+ }
+ }
}
+ // TODO: Remove this engine, tracking on b/123617158.
class DrawableEngine extends Engine {
private final Runnable mUnloadWallpaperCallback = () -> {
unloadWallpaper(false /* forgetSize */);
@@ -564,4 +635,35 @@ public class ImageWallpaper extends WallpaperService {
}
}
}
+
+ /**
+ * A listener to trace status of image wallpaper.
+ */
+ public interface WallpaperStatusListener {
+
+ /**
+ * Called back while ambient mode changes.
+ * @param inAmbientMode true if is in ambient mode, false otherwise.
+ * @param duration the duration of animation.
+ */
+ void onAmbientModeChanged(boolean inAmbientMode, long duration);
+
+ /**
+ * Called back while wallpaper offsets.
+ * @param xOffset The offset portion along x.
+ * @param yOffset The offset portion along y.
+ */
+ void onOffsetsChanged(float xOffset, float yOffset, Rect frame);
+ }
+
+ /**
+ * An abstraction for view of GLRenderer.
+ */
+ public interface ImageGLView {
+
+ /**
+ * Ask the view to render.
+ */
+ void render();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java
new file mode 100644
index 000000000000..d03b00bcfc85
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLProgram.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.glwallpaper;
+
+import static android.opengl.GLES20.GL_FRAGMENT_SHADER;
+import static android.opengl.GLES20.GL_VERTEX_SHADER;
+import static android.opengl.GLES20.glAttachShader;
+import static android.opengl.GLES20.glCompileShader;
+import static android.opengl.GLES20.glCreateProgram;
+import static android.opengl.GLES20.glCreateShader;
+import static android.opengl.GLES20.glGetAttribLocation;
+import static android.opengl.GLES20.glGetUniformLocation;
+import static android.opengl.GLES20.glLinkProgram;
+import static android.opengl.GLES20.glShaderSource;
+import static android.opengl.GLES20.glUseProgram;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+/**
+ * This class takes charge of linking shader codes and then return a handle for OpenGL ES program.
+ */
+class ImageGLProgram {
+ private static final String TAG = ImageGLProgram.class.getSimpleName();
+
+ private Context mContext;
+ private int mProgramHandle;
+
+ ImageGLProgram(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ private int loadShaderProgram(int vertexId, int fragmentId) {
+ final String vertexSrc = getShaderResource(vertexId);
+ final String fragmentSrc = getShaderResource(fragmentId);
+ final int vertexHandle = getShaderHandle(GL_VERTEX_SHADER, vertexSrc);
+ final int fragmentHandle = getShaderHandle(GL_FRAGMENT_SHADER, fragmentSrc);
+ return getProgramHandle(vertexHandle, fragmentHandle);
+ }
+
+ private String getShaderResource(int shaderId) {
+ Resources res = mContext.getResources();
+ StringBuilder code = new StringBuilder();
+
+ try (BufferedReader reader = new BufferedReader(
+ new InputStreamReader(res.openRawResource(shaderId)))) {
+ String nextLine;
+ while ((nextLine = reader.readLine()) != null) {
+ code.append(nextLine).append("\n");
+ }
+ } catch (IOException | Resources.NotFoundException ex) {
+ Log.d(TAG, "Can not read the shader source", ex);
+ code = null;
+ }
+
+ return code == null ? "" : code.toString();
+ }
+
+ private int getShaderHandle(int type, String src) {
+ final int shader = glCreateShader(type);
+ if (shader == 0) {
+ Log.d(TAG, "Create shader failed, type=" + type);
+ return 0;
+ }
+ glShaderSource(shader, src);
+ glCompileShader(shader);
+ return shader;
+ }
+
+ private int getProgramHandle(int vertexHandle, int fragmentHandle) {
+ final int program = glCreateProgram();
+ if (program == 0) {
+ Log.d(TAG, "Can not create OpenGL ES program");
+ return 0;
+ }
+
+ glAttachShader(program, vertexHandle);
+ glAttachShader(program, fragmentHandle);
+ glLinkProgram(program);
+ return program;
+ }
+
+ boolean useGLProgram(int vertexResId, int fragmentResId) {
+ mProgramHandle = loadShaderProgram(vertexResId, fragmentResId);
+ glUseProgram(mProgramHandle);
+ return true;
+ }
+
+ int getAttributeHandle(String name) {
+ return glGetAttribLocation(mProgramHandle, name);
+ }
+
+ int getUniformHandle(String name) {
+ return glGetUniformLocation(mProgramHandle, name);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
new file mode 100644
index 000000000000..19d85b155cba
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageGLWallpaper.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.glwallpaper;
+
+import static android.opengl.GLES20.GL_FLOAT;
+import static android.opengl.GLES20.GL_LINEAR;
+import static android.opengl.GLES20.GL_TEXTURE0;
+import static android.opengl.GLES20.GL_TEXTURE_2D;
+import static android.opengl.GLES20.GL_TEXTURE_MAG_FILTER;
+import static android.opengl.GLES20.GL_TEXTURE_MIN_FILTER;
+import static android.opengl.GLES20.GL_TRIANGLES;
+import static android.opengl.GLES20.glActiveTexture;
+import static android.opengl.GLES20.glBindTexture;
+import static android.opengl.GLES20.glDrawArrays;
+import static android.opengl.GLES20.glEnableVertexAttribArray;
+import static android.opengl.GLES20.glGenTextures;
+import static android.opengl.GLES20.glTexParameteri;
+import static android.opengl.GLES20.glUniform1i;
+import static android.opengl.GLES20.glVertexAttribPointer;
+
+import android.graphics.Bitmap;
+import android.opengl.GLUtils;
+import android.os.Build;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * This class takes charge of the geometry data like vertices and texture coordinates.
+ * It delivers these data to opengl runtime and triggers draw calls if necessary.
+ */
+class ImageGLWallpaper {
+ private static final String TAG = ImageGLWallpaper.class.getSimpleName();
+
+ static final String A_POSITION = "aPosition";
+ static final String A_TEXTURE_COORDINATES = "aTextureCoordinates";
+ static final String U_CENTER_REVEAL = "uCenterReveal";
+ static final String U_REVEAL = "uReveal";
+ static final String U_AOD2OPACITY = "uAod2Opacity";
+ static final String U_TEXTURE = "uTexture";
+
+ private static final int HANDLE_UNDEFINED = -1;
+ private static final int POSITION_COMPONENT_COUNT = 2;
+ private static final int TEXTURE_COMPONENT_COUNT = 2;
+ private static final int BYTES_PER_FLOAT = 4;
+
+ // Vertices to define the square with 2 triangles.
+ private static final float[] VERTICES = {
+ -1.0f, -1.0f,
+ +1.0f, -1.0f,
+ +1.0f, +1.0f,
+ +1.0f, +1.0f,
+ -1.0f, +1.0f,
+ -1.0f, -1.0f
+ };
+
+ // Texture coordinates that maps to vertices.
+ private static final float[] TEXTURES = {
+ 0f, 1f,
+ 1f, 1f,
+ 1f, 0f,
+ 1f, 0f,
+ 0f, 0f,
+ 0f, 1f
+ };
+
+ private final FloatBuffer mVertexBuffer;
+ private final FloatBuffer mTextureBuffer;
+ private final ImageGLProgram mProgram;
+
+ private int mAttrPosition;
+ private int mAttrTextureCoordinates;
+ private int mUniAod2Opacity;
+ private int mUniCenterReveal;
+ private int mUniReveal;
+ private int mUniTexture;
+ private int mTextureId;
+
+ ImageGLWallpaper(ImageGLProgram program) {
+ mProgram = program;
+
+ // Create an float array in opengles runtime (native) and put vertex data.
+ mVertexBuffer = ByteBuffer.allocateDirect(VERTICES.length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ mVertexBuffer.put(VERTICES);
+ mVertexBuffer.position(0);
+
+ // Create an float array in opengles runtime (native) and put texture data.
+ mTextureBuffer = ByteBuffer.allocateDirect(TEXTURES.length * BYTES_PER_FLOAT)
+ .order(ByteOrder.nativeOrder())
+ .asFloatBuffer();
+ mTextureBuffer.put(TEXTURES);
+ mTextureBuffer.position(0);
+ }
+
+ void setup() {
+ setupAttributes();
+ setupUniforms();
+ }
+
+ private void setupAttributes() {
+ mAttrPosition = mProgram.getAttributeHandle(A_POSITION);
+ mVertexBuffer.position(0);
+ glVertexAttribPointer(mAttrPosition, POSITION_COMPONENT_COUNT, GL_FLOAT,
+ false, 0, mVertexBuffer);
+ glEnableVertexAttribArray(mAttrPosition);
+
+ mAttrTextureCoordinates = mProgram.getAttributeHandle(A_TEXTURE_COORDINATES);
+ mTextureBuffer.position(0);
+ glVertexAttribPointer(mAttrTextureCoordinates, TEXTURE_COMPONENT_COUNT, GL_FLOAT,
+ false, 0, mTextureBuffer);
+ glEnableVertexAttribArray(mAttrTextureCoordinates);
+ }
+
+ private void setupUniforms() {
+ mUniAod2Opacity = mProgram.getUniformHandle(U_AOD2OPACITY);
+ mUniCenterReveal = mProgram.getUniformHandle(U_CENTER_REVEAL);
+ mUniReveal = mProgram.getUniformHandle(U_REVEAL);
+ mUniTexture = mProgram.getUniformHandle(U_TEXTURE);
+ }
+
+ int getHandle(String name) {
+ switch (name) {
+ case A_POSITION:
+ return mAttrPosition;
+ case A_TEXTURE_COORDINATES:
+ return mAttrTextureCoordinates;
+ case U_AOD2OPACITY:
+ return mUniAod2Opacity;
+ case U_CENTER_REVEAL:
+ return mUniCenterReveal;
+ case U_REVEAL:
+ return mUniReveal;
+ case U_TEXTURE:
+ return mUniTexture;
+ default:
+ return HANDLE_UNDEFINED;
+ }
+ }
+
+ void draw() {
+ glDrawArrays(GL_TRIANGLES, 0, VERTICES.length / 2);
+ }
+
+ void setupTexture(Bitmap bitmap) {
+ final int[] tids = new int[1];
+
+ if (bitmap == null) {
+ Log.w(TAG, "setupTexture: invalid bitmap");
+ return;
+ }
+
+ // Generate one texture object and store the id in tids[0].
+ glGenTextures(1, tids, 0);
+ if (tids[0] == 0) {
+ Log.w(TAG, "setupTexture: glGenTextures() failed");
+ return;
+ }
+
+ // Bind a named texture to a texturing target.
+ glBindTexture(GL_TEXTURE_2D, tids[0]);
+ // Load the bitmap data and copy it over into the texture object that is currently bound.
+ GLUtils.texImage2D(GL_TEXTURE_2D, 0, bitmap, 0);
+ // Use bilinear texture filtering when minification.
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ // Use bilinear texture filtering when magnification.
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ mTextureId = tids[0];
+ }
+
+ void useTexture() {
+ // Set the active texture unit to texture unit 0.
+ glActiveTexture(GL_TEXTURE0);
+ // Bind the texture to this unit.
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ // Let the texture sampler in fragment shader to read form this texture unit.
+ glUniform1i(mUniTexture, 0);
+ }
+
+ void adjustTextureCoordinates(Bitmap bitmap, int surfaceWidth, int surfaceHeight,
+ float xOffset, float yOffset) {
+ if (bitmap == null) {
+ Log.d(TAG, "adjustTextureCoordinates: invalid bitmap");
+ return;
+ }
+
+ int bitmapWidth = bitmap.getWidth();
+ int bitmapHeight = bitmap.getHeight();
+ float ratioW = 1f;
+ float ratioH = 1f;
+ float rX = 0f;
+ float rY = 0f;
+ float[] coordinates = null;
+
+ final boolean adjustWidth = bitmapWidth > surfaceWidth;
+ final boolean adjustHeight = bitmapHeight > surfaceHeight;
+
+ if (adjustWidth || adjustHeight) {
+ coordinates = TEXTURES.clone();
+ }
+
+ if (adjustWidth) {
+ float x = (float) Math.round((bitmapWidth - surfaceWidth) * xOffset) / bitmapWidth;
+ ratioW = (float) surfaceWidth / bitmapWidth;
+ float referenceX = x + ratioW > 1f ? 1f - ratioW : x;
+ for (int i = 0; i < coordinates.length; i += 2) {
+ if (i == 2 || i == 4 || i == 6) {
+ coordinates[i] = Math.min(1f, referenceX + ratioW);
+ } else {
+ coordinates[i] = referenceX;
+ }
+ }
+ rX = referenceX;
+ }
+
+
+ if (adjustHeight) {
+ float y = (float) Math.round((bitmapHeight - surfaceHeight) * yOffset) / bitmapHeight;
+ ratioH = (float) surfaceHeight / bitmapHeight;
+ float referenceY = y + ratioH > 1f ? 1f - ratioH : y;
+ for (int i = 1; i < coordinates.length; i += 2) {
+ if (i == 1 || i == 3 || i == 11) {
+ coordinates[i] = Math.min(1f, referenceY + ratioH);
+ } else {
+ coordinates[i] = referenceY;
+ }
+ }
+ rY = referenceY;
+ }
+
+ if (adjustWidth || adjustHeight) {
+ if (Build.IS_DEBUGGABLE) {
+ Log.d(TAG, "adjustTextureCoordinates: sW=" + surfaceWidth + ", sH=" + surfaceHeight
+ + ", bW=" + bitmapWidth + ", bH=" + bitmapHeight
+ + ", rW=" + ratioW + ", rH=" + ratioH + ", rX=" + rX + ", rY=" + rY);
+ }
+ mTextureBuffer.put(coordinates);
+ mTextureBuffer.position(0);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
new file mode 100644
index 000000000000..477e7d7ebf72
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageProcessHelper.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.glwallpaper;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * A helper class that computes histogram and percentile 85 from a bitmap.
+ * Percentile 85 will be computed each time the user picks a new image wallpaper.
+ */
+class ImageProcessHelper {
+ private static final String TAG = ImageProcessHelper.class.getSimpleName();
+ private static final float DEFAULT_PER85 = 0.8f;
+ private static final int MSG_UPDATE_PER85 = 1;
+
+ /**
+ * This color matrix will be applied to each pixel to get luminance from rgb by below formula:
+ * Luminance = .2126f * r + .7152f * g + .0722f * b.
+ */
+ private static final float[] LUMINOSITY_MATRIX = new float[] {
+ .2126f, .0000f, .0000f, .0000f, .0000f,
+ .0000f, .7152f, .0000f, .0000f, .0000f,
+ .0000f, .0000f, .0722f, .0000f, .0000f,
+ .0000f, .0000f, .0000f, 1.000f, .0000f
+ };
+
+ private final Handler mHandler = new Handler(new Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_PER85:
+ mPer85 = (float) msg.obj;
+ return true;
+ default:
+ return false;
+ }
+ }
+ });
+
+ private float mPer85 = DEFAULT_PER85;
+
+ void startComputingPercentile85(Bitmap bitmap) {
+ new Per85ComputeTask(mHandler).execute(bitmap);
+ }
+
+ float getPercentile85() {
+ return mPer85;
+ }
+
+ private static class Per85ComputeTask extends AsyncTask<Bitmap, Void, Float> {
+ private Handler mUpdateHandler;
+
+ Per85ComputeTask(Handler handler) {
+ super(handler);
+ mUpdateHandler = handler;
+ }
+
+ @Override
+ protected Float doInBackground(Bitmap... bitmaps) {
+ Bitmap bitmap = bitmaps[0];
+ if (bitmap != null) {
+ int[] histogram = processHistogram(bitmap);
+ return computePercentile85(bitmap, histogram);
+ }
+ Log.e(TAG, "Per85ComputeTask: Can't get bitmap");
+ return DEFAULT_PER85;
+ }
+
+ @Override
+ protected void onPostExecute(Float result) {
+ Message msg = mUpdateHandler.obtainMessage(MSG_UPDATE_PER85, result);
+ mUpdateHandler.sendMessage(msg);
+ }
+
+ private int[] processHistogram(Bitmap bitmap) {
+ int width = bitmap.getWidth();
+ int height = bitmap.getHeight();
+
+ Bitmap target = Bitmap.createBitmap(width, height, bitmap.getConfig());
+ Canvas canvas = new Canvas(target);
+ ColorMatrix cm = new ColorMatrix(LUMINOSITY_MATRIX);
+ Paint paint = new Paint();
+ paint.setColorFilter(new ColorMatrixColorFilter(cm));
+ canvas.drawBitmap(bitmap, new Matrix(), paint);
+
+ // TODO: Fine tune the performance here, tracking on b/123615079.
+ int[] histogram = new int[256];
+ for (int row = 0; row < height; row++) {
+ for (int col = 0; col < width; col++) {
+ int pixel = target.getPixel(col, row);
+ int y = Color.red(pixel) + Color.green(pixel) + Color.blue(pixel);
+ histogram[y]++;
+ }
+ }
+
+ return histogram;
+ }
+
+ private float computePercentile85(Bitmap bitmap, int[] histogram) {
+ float per85 = DEFAULT_PER85;
+ int pixelCount = bitmap.getWidth() * bitmap.getHeight();
+ float[] acc = new float[256];
+ for (int i = 0; i < acc.length; i++) {
+ acc[i] = (float) histogram[i] / pixelCount;
+ float prev = i == 0 ? 0f : acc[i - 1];
+ float next = acc[i];
+ float idx = (float) (i + 1) / 255;
+ float sum = prev + next;
+ if (prev < 0.85f && sum >= 0.85f) {
+ per85 = idx;
+ }
+ if (i > 0) {
+ acc[i] += acc[i - 1];
+ }
+ }
+ return per85;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
new file mode 100644
index 000000000000..5914236ab349
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.glwallpaper;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import com.android.systemui.Interpolators;
+
+/**
+ * Use ValueAnimator and appropriate interpolator to control the progress of reveal transition.
+ * The transition will happen while getting awake and quit events.
+ */
+class ImageRevealHelper {
+ private static final String TAG = ImageRevealHelper.class.getSimpleName();
+ private static final float MAX_REVEAL = 0f;
+ private static final float MIN_REVEAL = 1f;
+
+ private final ValueAnimator mAnimator;
+ private final RevealStateListener mRevealListener;
+ private float mReveal = MAX_REVEAL;
+ private boolean mAwake = false;
+
+ ImageRevealHelper(RevealStateListener listener) {
+ mRevealListener = listener;
+ mAnimator = ValueAnimator.ofFloat();
+ mAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
+ mAnimator.addUpdateListener(animator -> {
+ mReveal = (float) animator.getAnimatedValue();
+ if (mRevealListener != null) {
+ mRevealListener.onRevealStateChanged();
+ }
+ });
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ private boolean mIsCanceled;
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mIsCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!mIsCanceled) {
+ mAwake = !mAwake;
+ }
+ mIsCanceled = false;
+ }
+ });
+ }
+
+ private void animate() {
+ mAnimator.cancel();
+ mAnimator.setFloatValues(mReveal, !mAwake ? MIN_REVEAL : MAX_REVEAL);
+ mAnimator.start();
+ }
+
+ public float getReveal() {
+ return mReveal;
+ }
+
+ public boolean isAwake() {
+ return mAwake;
+ }
+
+ void updateAwake(boolean awake, long duration) {
+ mAwake = awake;
+ mAnimator.setDuration(duration);
+ animate();
+ }
+
+ /**
+ * A listener to trace value changes of reveal.
+ */
+ public interface RevealStateListener {
+
+ /**
+ * Called back while reveal status changes.
+ */
+ void onRevealStateChanged();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
new file mode 100644
index 000000000000..991b1161dde2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.glwallpaper;
+
+import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
+import static android.opengl.GLES20.glClear;
+import static android.opengl.GLES20.glClearColor;
+import static android.opengl.GLES20.glUniform1f;
+import static android.opengl.GLES20.glViewport;
+
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Rect;
+import android.opengl.GLSurfaceView;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.systemui.ImageWallpaper;
+import com.android.systemui.ImageWallpaper.ImageGLView;
+import com.android.systemui.R;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * A GL renderer for image wallpaper.
+ */
+public class ImageWallpaperRenderer implements GLSurfaceView.Renderer,
+ ImageWallpaper.WallpaperStatusListener, ImageRevealHelper.RevealStateListener {
+ private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
+
+ private final WallpaperManager mWallpaperManager;
+ private final ImageGLProgram mProgram;
+ private final ImageGLWallpaper mWallpaper;
+ private final ImageProcessHelper mImageProcessHelper;
+ private final ImageRevealHelper mImageRevealHelper;
+ private final ImageGLView mGLView;
+ private float mXOffset = 0f;
+ private float mYOffset = 0f;
+
+ public ImageWallpaperRenderer(Context context, ImageGLView glView) {
+ mWallpaperManager = context.getSystemService(WallpaperManager.class);
+ if (mWallpaperManager == null) {
+ Log.w(TAG, "WallpaperManager not available");
+ }
+
+ mProgram = new ImageGLProgram(context);
+ mWallpaper = new ImageGLWallpaper(mProgram);
+ mImageProcessHelper = new ImageProcessHelper();
+ mImageRevealHelper = new ImageRevealHelper(this);
+ mGLView = glView;
+
+ if (mWallpaperManager != null) {
+ // Compute per85 as transition threshold, this is an async work.
+ mImageProcessHelper.startComputingPercentile85(mWallpaperManager.getBitmap());
+ }
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ glClearColor(0f, 0f, 0f, 1.0f);
+ mProgram.useGLProgram(
+ R.raw.image_wallpaper_vertex_shader, R.raw.image_wallpaper_fragment_shader);
+ mWallpaper.setup();
+ mWallpaper.setupTexture(mWallpaperManager.getBitmap());
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ glViewport(0, 0, width, height);
+ if (Build.IS_DEBUGGABLE) {
+ Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height
+ + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
+ }
+ mWallpaper.adjustTextureCoordinates(mWallpaperManager.getBitmap(),
+ width, height, mXOffset, mYOffset);
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ float threshold = mImageProcessHelper.getPercentile85();
+ float reveal = mImageRevealHelper.getReveal();
+
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_AOD2OPACITY), 1);
+ glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_CENTER_REVEAL), threshold);
+ glUniform1f(mWallpaper.getHandle(ImageGLWallpaper.U_REVEAL), reveal);
+
+ mWallpaper.useTexture();
+ mWallpaper.draw();
+ }
+
+ @Override
+ public void onAmbientModeChanged(boolean inAmbientMode, long duration) {
+ mImageRevealHelper.updateAwake(!inAmbientMode, duration);
+ requestRender();
+ }
+
+ @Override
+ public void onOffsetsChanged(float xOffset, float yOffset, Rect frame) {
+ if (frame == null || mWallpaperManager == null
+ || (xOffset == mXOffset && yOffset == mYOffset)) {
+ return;
+ }
+
+ Bitmap bitmap = mWallpaperManager.getBitmap();
+ if (bitmap == null) {
+ return;
+ }
+
+ int width = frame.width();
+ int height = frame.height();
+ mXOffset = xOffset;
+ mYOffset = yOffset;
+
+ if (Build.IS_DEBUGGABLE) {
+ Log.d(TAG, "onOffsetsChanged: width=" + width + ", height=" + height
+ + ", xOffset=" + mXOffset + ", yOffset=" + mYOffset);
+ }
+ mWallpaper.adjustTextureCoordinates(bitmap, width, height, mXOffset, mYOffset);
+ requestRender();
+ }
+
+ @Override
+ public void onRevealStateChanged() {
+ requestRender();
+ }
+
+ private void requestRender() {
+ if (mGLView != null) {
+ mGLView.render();
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 30d5b653deba..008eeefa8341 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -479,8 +479,7 @@ public class StatusBar extends SystemUI implements DemoMode,
updateAodMaskVisibility(deviceSupportsAodWallpaper && aodImageWallpaperEnabled);
// If WallpaperInfo is null, it must be ImageWallpaper.
final boolean supportsAmbientMode = deviceSupportsAodWallpaper
- && (info == null && aodImageWallpaperEnabled
- || info != null && info.supportsAmbientMode());
+ && (info == null || info.supportsAmbientMode());
mStatusBarWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode);
diff --git a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
index 6ee341dd974c..f446cefb7b6f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpaper/AodMaskView.java
@@ -156,6 +156,8 @@ public class AodMaskView extends ImageView implements StatusBarStateController.S
private boolean checkIfNeedMask() {
// We need mask for ImageWallpaper / LockScreen Wallpaper (Music album art).
+ // Because of conflicting with another wallpaper feature,
+ // we only support LockScreen wallpaper currently.
return mWallpaperManager.getWallpaperInfo() == null || ScrimState.AOD.hasBackdrop();
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index b0ef8a0d4209..071dde74f103 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2243,12 +2243,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
synchronized (mLock) {
mInAmbientMode = inAmbientMode;
final WallpaperData data = mWallpaperMap.get(mCurrentUserId);
- final boolean hasConnection = data != null && data.connection != null;
- final WallpaperInfo info = hasConnection ? data.connection.mInfo : null;
-
// The wallpaper info is null for image wallpaper, also use the engine in this case.
- if (hasConnection && (info == null && isAodImageWallpaperEnabled()
- || info != null && info.supportsAmbientMode())) {
+ if (data != null && data.connection != null && (data.connection.mInfo == null
+ || data.connection.mInfo.supportsAmbientMode())) {
// TODO(multi-display) Extends this method with specific display.
engine = data.connection.getDisplayConnectorOrCreate(DEFAULT_DISPLAY).mEngine;
} else {