• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.wallpaper.util;
17 
18 import static android.view.View.MeasureSpec.EXACTLY;
19 import static android.view.View.MeasureSpec.makeMeasureSpec;
20 
21 import android.content.Context;
22 import android.graphics.RenderEffect;
23 import android.graphics.Shader;
24 import android.service.wallpaper.WallpaperService;
25 import android.util.Log;
26 import android.view.Surface;
27 import android.view.SurfaceControlViewHost;
28 import android.view.SurfaceHolder;
29 import android.view.SurfaceView;
30 import android.view.View;
31 import android.widget.ImageView;
32 
33 import androidx.annotation.Nullable;
34 
35 import com.android.wallpaper.model.WallpaperInfo.ColorInfo;
36 import com.android.wallpaper.module.Injector;
37 import com.android.wallpaper.module.InjectorProvider;
38 import com.android.wallpaper.module.PackageStatusNotifier;
39 
40 import java.util.concurrent.ExecutionException;
41 import java.util.concurrent.Future;
42 
43 /**
44  * Default implementation of {@link SurfaceHolder.Callback} to render a static wallpaper when the
45  * surface has been created.
46  */
47 public class WallpaperSurfaceCallback implements SurfaceHolder.Callback {
48 
49     public static final float LOW_RES_BITMAP_BLUR_RADIUS = 150f;
50 
51     /**
52      * Listener used to be notified when this surface is created
53      */
54     public interface SurfaceListener {
55         /**
56          * Called when {@link WallpaperSurfaceCallback#surfaceCreated(SurfaceHolder)} is called.
57          */
onSurfaceCreated()58         void onSurfaceCreated();
59     }
60 
61     private static final String TAG = "WallpaperSurfaceCallback";
62     private Surface mLastSurface;
63     private SurfaceControlViewHost mHost;
64     // Home workspace surface is behind the app window, and so must the home image wallpaper like
65     // the live wallpaper. This view is rendered on here for home image wallpaper.
66     private ImageView mHomeImageWallpaper;
67     private final Context mContext;
68     private final View mContainerView;
69     private final SurfaceView mWallpaperSurface;
70     @Nullable
71     private final SurfaceListener mListener;
72     @Nullable
73     private final Future<ColorInfo> mColorFuture;
74     private boolean mSurfaceCreated;
75 
76     private PackageStatusNotifier.Listener mAppStatusListener;
77     private PackageStatusNotifier mPackageStatusNotifier;
78 
WallpaperSurfaceCallback(Context context, View containerView, SurfaceView wallpaperSurface, @Nullable Future<ColorInfo> colorFuture, @Nullable SurfaceListener listener)79     public WallpaperSurfaceCallback(Context context, View containerView,
80             SurfaceView wallpaperSurface, @Nullable Future<ColorInfo> colorFuture,
81             @Nullable SurfaceListener listener) {
82         mContext = context.getApplicationContext();
83         mContainerView = containerView;
84         mWallpaperSurface = wallpaperSurface;
85         mListener = listener;
86 
87         // Notify WallpaperSurface to reset image wallpaper when encountered live wallpaper's
88         // package been changed in background.
89         Injector injector = InjectorProvider.getInjector();
90         mPackageStatusNotifier = injector.getPackageStatusNotifier(context);
91         mAppStatusListener = (packageName, status) -> {
92             if (status != PackageStatusNotifier.PackageStatus.REMOVED) {
93                 resetHomeImageWallpaper();
94             }
95         };
96         mPackageStatusNotifier.addListener(mAppStatusListener,
97                 WallpaperService.SERVICE_INTERFACE);
98         mColorFuture = colorFuture;
99     }
100 
WallpaperSurfaceCallback(Context context, View containerView, SurfaceView wallpaperSurface, @Nullable SurfaceListener listener)101     public WallpaperSurfaceCallback(Context context, View containerView,
102             SurfaceView wallpaperSurface, @Nullable SurfaceListener listener) {
103         this(context, containerView, wallpaperSurface, /* colorFuture= */ null, listener);
104     }
105 
WallpaperSurfaceCallback(Context context, View containerView, SurfaceView wallpaperSurface)106     public WallpaperSurfaceCallback(Context context, View containerView,
107             SurfaceView wallpaperSurface) {
108         this(context, containerView, wallpaperSurface, null);
109     }
110 
111     @Override
surfaceCreated(SurfaceHolder holder)112     public void surfaceCreated(SurfaceHolder holder) {
113         if (mLastSurface != holder.getSurface()) {
114             mLastSurface = holder.getSurface();
115             setupSurfaceWallpaper(/* forceClean= */ true);
116         }
117         if (mListener != null) {
118             mListener.onSurfaceCreated();
119         }
120         mSurfaceCreated = true;
121     }
122 
123     @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)124     public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
125 
126     @Override
surfaceDestroyed(SurfaceHolder holder)127     public void surfaceDestroyed(SurfaceHolder holder) {
128         mSurfaceCreated = false;
129     }
130 
131     /**
132      * Call to release resources and app status listener.
133      */
cleanUp()134     public void cleanUp() {
135         releaseHost();
136         if (mHomeImageWallpaper != null) {
137             mHomeImageWallpaper.setImageDrawable(null);
138         }
139         mPackageStatusNotifier.removeListener(mAppStatusListener);
140     }
141 
releaseHost()142     private void releaseHost() {
143         if (mHost != null) {
144             mHost.release();
145             mHost = null;
146         }
147     }
148 
149     /**
150      * Reset existing image wallpaper by creating a new ImageView for SurfaceControlViewHost
151      * if surface state is not created.
152      */
resetHomeImageWallpaper()153     private void resetHomeImageWallpaper() {
154         if (mSurfaceCreated) {
155             return;
156         }
157 
158         if (mHost != null) {
159             setupSurfaceWallpaper(/* forceClean= */ false);
160         }
161     }
162 
setupSurfaceWallpaper(boolean forceClean)163     private void setupSurfaceWallpaper(boolean forceClean) {
164         mHomeImageWallpaper = new ImageView(mContext);
165         Integer placeholder = null;
166         if (mColorFuture != null && mColorFuture.isDone()) {
167             try {
168                 ColorInfo colorInfo = mColorFuture.get();
169                 if (colorInfo != null) {
170                     placeholder = colorInfo.getPlaceholderColor();
171                 }
172             } catch (InterruptedException | ExecutionException e) {
173                 Log.e(TAG, "Couldn't get placeholder from ColorInfo.");
174             }
175         }
176         mHomeImageWallpaper.setBackgroundColor((placeholder != null) ? placeholder
177                 : ResourceUtils.getColorAttr(mContext, android.R.attr.colorSecondary));
178         mHomeImageWallpaper.measure(makeMeasureSpec(mContainerView.getWidth(), EXACTLY),
179                 makeMeasureSpec(mContainerView.getHeight(), EXACTLY));
180         mHomeImageWallpaper.layout(0, 0, mContainerView.getWidth(),
181                 mContainerView.getHeight());
182         if (forceClean) {
183             releaseHost();
184             mHost = new SurfaceControlViewHost(mContext,
185                     mContainerView.getDisplay(), mWallpaperSurface.getHostToken());
186         }
187         mHost.setView(mHomeImageWallpaper, mHomeImageWallpaper.getWidth(),
188                 mHomeImageWallpaper.getHeight());
189         mWallpaperSurface.setChildSurfacePackage(mHost.getSurfacePackage());
190     }
191 
192     @Nullable
getHomeImageWallpaper()193     public ImageView getHomeImageWallpaper() {
194         return mHomeImageWallpaper;
195     }
196 
197     /**
198      * @param blur whether to blur the home image wallpaper
199      */
setHomeImageWallpaperBlur(boolean blur)200     public void setHomeImageWallpaperBlur(boolean blur) {
201         if (mHomeImageWallpaper == null) {
202             return;
203         }
204         if (blur) {
205             mHomeImageWallpaper.setRenderEffect(
206                     RenderEffect.createBlurEffect(LOW_RES_BITMAP_BLUR_RADIUS,
207                             LOW_RES_BITMAP_BLUR_RADIUS, Shader.TileMode.CLAMP));
208         } else {
209             mHomeImageWallpaper.setRenderEffect(null);
210         }
211     }
212 }
213