blob: fdc3a947a3e5e3284ae105fe43004d97b2d2944d [file] [log] [blame]
/*
* 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);
}
}