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.service.wallpaper.WallpaperService; 23 import android.view.Surface; 24 import android.view.SurfaceControlViewHost; 25 import android.view.SurfaceHolder; 26 import android.view.SurfaceView; 27 import android.view.View; 28 import android.widget.ImageView; 29 30 import androidx.annotation.Nullable; 31 32 import com.android.wallpaper.module.Injector; 33 import com.android.wallpaper.module.InjectorProvider; 34 import com.android.wallpaper.module.PackageStatusNotifier; 35 36 import java.util.concurrent.ExecutionException; 37 import java.util.concurrent.Future; 38 39 /** 40 * Default implementation of {@link SurfaceHolder.Callback} to render a static wallpaper when the 41 * surface has been created. 42 */ 43 public class WallpaperSurfaceCallback implements SurfaceHolder.Callback { 44 45 /** 46 * Listener used to be notified when this surface is created 47 */ 48 public interface SurfaceListener { 49 /** 50 * Called when {@link WallpaperSurfaceCallback#surfaceCreated(SurfaceHolder)} is called. 51 */ onSurfaceCreated()52 void onSurfaceCreated(); 53 } 54 55 private Surface mLastSurface; 56 private SurfaceControlViewHost mHost; 57 // Home workspace surface is behind the app window, and so must the home image wallpaper like 58 // the live wallpaper. This view is rendered on here for home image wallpaper. 59 private ImageView mHomeImageWallpaper; 60 private final Context mContext; 61 private final View mContainerView; 62 private final SurfaceView mWallpaperSurface; 63 @Nullable 64 private final SurfaceListener mListener; 65 @Nullable 66 private final Future<Integer> mPlaceholderColor; 67 private boolean mSurfaceCreated; 68 69 private PackageStatusNotifier.Listener mAppStatusListener; 70 private PackageStatusNotifier mPackageStatusNotifier; 71 WallpaperSurfaceCallback(Context context, View containerView, SurfaceView wallpaperSurface, @Nullable Future<Integer> placeholderColor, @Nullable SurfaceListener listener)72 public WallpaperSurfaceCallback(Context context, View containerView, 73 SurfaceView wallpaperSurface, @Nullable Future<Integer> placeholderColor, 74 @Nullable SurfaceListener listener) { 75 mContext = context.getApplicationContext(); 76 mContainerView = containerView; 77 mWallpaperSurface = wallpaperSurface; 78 mListener = listener; 79 80 // Notify WallpaperSurface to reset image wallpaper when encountered live wallpaper's 81 // package been changed in background. 82 Injector injector = InjectorProvider.getInjector(); 83 mPackageStatusNotifier = injector.getPackageStatusNotifier(context); 84 mAppStatusListener = (packageName, status) -> { 85 if (status != PackageStatusNotifier.PackageStatus.REMOVED) { 86 resetHomeImageWallpaper(); 87 } 88 }; 89 mPackageStatusNotifier.addListener(mAppStatusListener, 90 WallpaperService.SERVICE_INTERFACE); 91 mPlaceholderColor = placeholderColor; 92 } 93 WallpaperSurfaceCallback(Context context, View containerView, SurfaceView wallpaperSurface, @Nullable SurfaceListener listener)94 public WallpaperSurfaceCallback(Context context, View containerView, 95 SurfaceView wallpaperSurface, @Nullable SurfaceListener listener) { 96 this(context, containerView, wallpaperSurface, null, listener); 97 } 98 WallpaperSurfaceCallback(Context context, View containerView, SurfaceView wallpaperSurface)99 public WallpaperSurfaceCallback(Context context, View containerView, 100 SurfaceView wallpaperSurface) { 101 this(context, containerView, wallpaperSurface, null); 102 } 103 104 @Override surfaceCreated(SurfaceHolder holder)105 public void surfaceCreated(SurfaceHolder holder) { 106 if (mLastSurface != holder.getSurface()) { 107 mLastSurface = holder.getSurface(); 108 setupSurfaceWallpaper(/* forceClean= */ true); 109 } 110 if (mListener != null) { 111 mListener.onSurfaceCreated(); 112 } 113 mSurfaceCreated = true; 114 } 115 116 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)117 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } 118 119 @Override surfaceDestroyed(SurfaceHolder holder)120 public void surfaceDestroyed(SurfaceHolder holder) { 121 mSurfaceCreated = false; 122 } 123 124 /** 125 * Call to release resources and app status listener. 126 */ cleanUp()127 public void cleanUp() { 128 releaseHost(); 129 if (mHomeImageWallpaper != null) { 130 mHomeImageWallpaper.setImageDrawable(null); 131 } 132 mPackageStatusNotifier.removeListener(mAppStatusListener); 133 } 134 releaseHost()135 private void releaseHost() { 136 if (mHost != null) { 137 mHost.release(); 138 mHost = null; 139 } 140 } 141 142 /** 143 * Reset existing image wallpaper by creating a new ImageView for SurfaceControlViewHost 144 * if surface state is not created. 145 */ resetHomeImageWallpaper()146 private void resetHomeImageWallpaper() { 147 if (mSurfaceCreated) { 148 return; 149 } 150 151 if (mHost != null) { 152 setupSurfaceWallpaper(/* forceClean= */ false); 153 } 154 } 155 setupSurfaceWallpaper(boolean forceClean)156 private void setupSurfaceWallpaper(boolean forceClean) { 157 mHomeImageWallpaper = new ImageView(mContext); 158 Integer placeholder = null; 159 if (mPlaceholderColor != null && mPlaceholderColor.isDone()) { 160 try { 161 placeholder = mPlaceholderColor.get(); 162 } catch (InterruptedException | ExecutionException e) { 163 // Ignore 164 } 165 } 166 mHomeImageWallpaper.setBackgroundColor((placeholder != null) ? placeholder 167 : ResourceUtils.getColorAttr(mContext, android.R.attr.colorSecondary)); 168 mHomeImageWallpaper.measure(makeMeasureSpec(mContainerView.getWidth(), EXACTLY), 169 makeMeasureSpec(mContainerView.getHeight(), EXACTLY)); 170 mHomeImageWallpaper.layout(0, 0, mContainerView.getWidth(), 171 mContainerView.getHeight()); 172 if (forceClean) { 173 releaseHost(); 174 mHost = new SurfaceControlViewHost(mContext, 175 mContainerView.getDisplay(), mWallpaperSurface.getHostToken()); 176 } 177 mHost.setView(mHomeImageWallpaper, mHomeImageWallpaper.getWidth(), 178 mHomeImageWallpaper.getHeight()); 179 mWallpaperSurface.setChildSurfacePackage(mHost.getSurfacePackage()); 180 } 181 182 @Nullable getHomeImageWallpaper()183 public ImageView getHomeImageWallpaper() { 184 return mHomeImageWallpaper; 185 } 186 } 187