/* * Copyright (C) 2020 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.launcher3.graphics; import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.content.Context; import android.hardware.display.DisplayManager; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.view.Display; import android.view.SurfaceControlViewHost; import android.view.View; import android.view.animation.AccelerateDecelerateInterpolator; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.model.GridSizeMigrationTaskV2; import java.util.concurrent.TimeUnit; /** Render preview using surface view. */ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient { private static final int FADE_IN_ANIMATION_DURATION = 200; private static final String KEY_HOST_TOKEN = "host_token"; private static final String KEY_VIEW_WIDTH = "width"; private static final String KEY_VIEW_HEIGHT = "height"; private static final String KEY_DISPLAY_ID = "display_id"; private static final String KEY_SURFACE_PACKAGE = "surface_package"; private static final String KEY_CALLBACK = "callback"; private final Context mContext; private final InvariantDeviceProfile mIdp; private final IBinder mHostToken; private final int mWidth; private final int mHeight; private final Display mDisplay; private SurfaceControlViewHost mSurfaceControlViewHost; PreviewSurfaceRenderer(Context context, Bundle bundle) { mContext = context; String gridName = bundle.getString("name"); bundle.remove("name"); if (gridName == null) { gridName = InvariantDeviceProfile.getCurrentGridName(context); } mIdp = new InvariantDeviceProfile(context, gridName); mHostToken = bundle.getBinder(KEY_HOST_TOKEN); mWidth = bundle.getInt(KEY_VIEW_WIDTH); mHeight = bundle.getInt(KEY_VIEW_HEIGHT); final DisplayManager displayManager = (DisplayManager) context.getSystemService( Context.DISPLAY_SERVICE); mDisplay = displayManager.getDisplay(bundle.getInt(KEY_DISPLAY_ID)); } /** Handle a received surface view request. */ Bundle render() { if (mSurfaceControlViewHost != null) { binderDied(); } SurfaceControlViewHost.SurfacePackage surfacePackage; try { mSurfaceControlViewHost = MAIN_EXECUTOR .submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken)) .get(5, TimeUnit.SECONDS); surfacePackage = mSurfaceControlViewHost.getSurfacePackage(); mHostToken.linkToDeath(this, 0); } catch (Exception e) { e.printStackTrace(); return null; } MODEL_EXECUTOR.post(() -> { final boolean success = doGridMigrationIfNecessary(); MAIN_EXECUTOR.post(() -> { // If mSurfaceControlViewHost is null due to any reason (e.g. binder died, // happening when user leaves the preview screen before preview rendering finishes), // we should return here. SurfaceControlViewHost host = mSurfaceControlViewHost; if (host == null) { return; } View view = new LauncherPreviewRenderer(mContext, mIdp, success).getRenderedView(); // This aspect scales the view to fit in the surface and centers it final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(), mHeight / (float) view.getMeasuredHeight()); view.setScaleX(scale); view.setScaleY(scale); view.setPivotX(0); view.setPivotY(0); view.setTranslationX((mWidth - scale * view.getWidth()) / 2); view.setTranslationY((mHeight - scale * view.getHeight()) / 2); view.setAlpha(0); view.animate().alpha(1) .setInterpolator(new AccelerateDecelerateInterpolator()) .setDuration(FADE_IN_ANIMATION_DURATION) .start(); host.setView(view, view.getMeasuredWidth(), view.getMeasuredHeight()); }); }); Bundle result = new Bundle(); result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage); Handler handler = new Handler(Looper.getMainLooper(), message -> { binderDied(); return true; }); Messenger messenger = new Messenger(handler); Message msg = Message.obtain(); msg.replyTo = messenger; result.putParcelable(KEY_CALLBACK, msg); return result; } @Override public void binderDied() { if (mSurfaceControlViewHost != null) { MAIN_EXECUTOR.execute(() -> { mSurfaceControlViewHost.release(); mSurfaceControlViewHost = null; }); } mHostToken.unlinkToDeath(this, 0); } private boolean doGridMigrationIfNecessary() { boolean needsToMigrate = MULTI_DB_GRID_MIRATION_ALGO.get() ? GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp) : GridSizeMigrationTask.needsToMigrate(mContext, mIdp); if (!needsToMigrate) { return false; } return MULTI_DB_GRID_MIRATION_ALGO.get() ? GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp) : GridSizeMigrationTask.migrateGridIfNeeded(mContext, mIdp); } }