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 17 package com.android.launcher3.graphics; 18 19 import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO; 20 import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; 21 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 22 23 import android.content.Context; 24 import android.hardware.display.DisplayManager; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.IBinder; 28 import android.os.Looper; 29 import android.os.Message; 30 import android.os.Messenger; 31 import android.view.Display; 32 import android.view.SurfaceControlViewHost; 33 import android.view.View; 34 import android.view.animation.AccelerateDecelerateInterpolator; 35 36 import com.android.launcher3.InvariantDeviceProfile; 37 import com.android.launcher3.model.GridSizeMigrationTask; 38 import com.android.launcher3.model.GridSizeMigrationTaskV2; 39 40 import java.util.concurrent.TimeUnit; 41 42 /** Render preview using surface view. */ 43 public class PreviewSurfaceRenderer implements IBinder.DeathRecipient { 44 45 private static final int FADE_IN_ANIMATION_DURATION = 200; 46 47 private static final String KEY_HOST_TOKEN = "host_token"; 48 private static final String KEY_VIEW_WIDTH = "width"; 49 private static final String KEY_VIEW_HEIGHT = "height"; 50 private static final String KEY_DISPLAY_ID = "display_id"; 51 private static final String KEY_SURFACE_PACKAGE = "surface_package"; 52 private static final String KEY_CALLBACK = "callback"; 53 54 private final Context mContext; 55 private final InvariantDeviceProfile mIdp; 56 private final IBinder mHostToken; 57 private final int mWidth; 58 private final int mHeight; 59 private final Display mDisplay; 60 61 private SurfaceControlViewHost mSurfaceControlViewHost; 62 PreviewSurfaceRenderer(Context context, Bundle bundle)63 PreviewSurfaceRenderer(Context context, Bundle bundle) { 64 mContext = context; 65 66 String gridName = bundle.getString("name"); 67 bundle.remove("name"); 68 if (gridName == null) { 69 gridName = InvariantDeviceProfile.getCurrentGridName(context); 70 } 71 mIdp = new InvariantDeviceProfile(context, gridName); 72 73 mHostToken = bundle.getBinder(KEY_HOST_TOKEN); 74 mWidth = bundle.getInt(KEY_VIEW_WIDTH); 75 mHeight = bundle.getInt(KEY_VIEW_HEIGHT); 76 77 final DisplayManager displayManager = (DisplayManager) context.getSystemService( 78 Context.DISPLAY_SERVICE); 79 mDisplay = displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID)); 80 } 81 82 /** Handle a received surface view request. */ render()83 Bundle render() { 84 if (mSurfaceControlViewHost != null) { 85 binderDied(); 86 } 87 88 SurfaceControlViewHost.SurfacePackage surfacePackage; 89 try { 90 mSurfaceControlViewHost = MAIN_EXECUTOR 91 .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken)) 92 .get(5, TimeUnit.SECONDS); 93 surfacePackage = mSurfaceControlViewHost.getSurfacePackage(); 94 mHostToken.linkToDeath(this, 0); 95 } catch (Exception e) { 96 e.printStackTrace(); 97 return null; 98 } 99 100 MODEL_EXECUTOR.post(() -> { 101 final boolean success = doGridMigrationIfNecessary(); 102 103 MAIN_EXECUTOR.post(() -> { 104 // If mSurfaceControlViewHost is null due to any reason (e.g. binder died, 105 // happening when user leaves the preview screen before preview rendering finishes), 106 // we should return here. 107 SurfaceControlViewHost host = mSurfaceControlViewHost; 108 if (host == null) { 109 return; 110 } 111 112 View view = new LauncherPreviewRenderer(mContext, mIdp, success).getRenderedView(); 113 // This aspect scales the view to fit in the surface and centers it 114 final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(), 115 mHeight / (float) view.getMeasuredHeight()); 116 view.setScaleX(scale); 117 view.setScaleY(scale); 118 view.setPivotX(0); 119 view.setPivotY(0); 120 view.setTranslationX((mWidth - scale * view.getWidth()) / 2); 121 view.setTranslationY((mHeight - scale * view.getHeight()) / 2); 122 view.setAlpha(0); 123 view.animate().alpha(1) 124 .setInterpolator(new AccelerateDecelerateInterpolator()) 125 .setDuration(FADE_IN_ANIMATION_DURATION) 126 .start(); 127 host.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight()); 128 }); 129 }); 130 131 Bundle result = new Bundle(); 132 result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage); 133 134 Handler handler = new Handler(Looper.getMainLooper(), message -> { 135 binderDied(); 136 return true; 137 }); 138 Messenger messenger = new Messenger(handler); 139 Message msg = Message.obtain(); 140 msg.replyTo = messenger; 141 result.putParcelable(KEY_CALLBACK, msg); 142 return result; 143 } 144 145 @Override binderDied()146 public void binderDied() { 147 if (mSurfaceControlViewHost != null) { 148 MAIN_EXECUTOR.execute(() -> { 149 mSurfaceControlViewHost.release(); 150 mSurfaceControlViewHost = null; 151 }); 152 } 153 mHostToken.unlinkToDeath(this, 0); 154 } 155 doGridMigrationIfNecessary()156 private boolean doGridMigrationIfNecessary() { 157 boolean needsToMigrate = 158 MULTI_DB_GRID_MIRATION_ALGO.get() 159 ? GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp) 160 : GridSizeMigrationTask.needsToMigrate(mContext, mIdp); 161 if (!needsToMigrate) { 162 return false; 163 } 164 return MULTI_DB_GRID_MIRATION_ALGO.get() 165 ? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp) 166 : GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp); 167 } 168 } 169