Adding home animation support for non-system Launcher am: 30ac97d938
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/12150115 Change-Id: I225841557201a377e74c1b380beeea65faa30a61
This commit is contained in:
+135
-3
@@ -15,14 +15,35 @@
|
||||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static android.content.Intent.EXTRA_COMPONENT_NAME;
|
||||
import static android.content.Intent.EXTRA_USER;
|
||||
|
||||
import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT;
|
||||
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION;
|
||||
import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE;
|
||||
import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
|
||||
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Messenger;
|
||||
import android.os.ParcelUuid;
|
||||
import android.os.UserHandle;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControl.Transaction;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
@@ -32,19 +53,33 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.anim.SpringAnimationBuilder;
|
||||
import com.android.quickstep.fallback.FallbackRecentsView;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.util.TransformParams.BuilderProxy;
|
||||
import com.android.systemui.shared.recents.model.Task.TaskKey;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.InputConsumerController;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Handles the navigation gestures when a 3rd party launcher is the default home activity.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
public class FallbackSwipeHandler extends
|
||||
BaseSwipeUpHandlerV2<RecentsActivity, FallbackRecentsView> {
|
||||
|
||||
/**
|
||||
* Message used for receiving gesture nav contract information. We use a static messenger to
|
||||
* avoid leaking too make binders in case the receiving launcher does not handle the contract
|
||||
* properly.
|
||||
*/
|
||||
private static StaticMessageReceiver sMessageReceiver = null;
|
||||
|
||||
private FallbackHomeAnimationFactory mActiveAnimationFactory;
|
||||
private final boolean mRunningOverHome;
|
||||
|
||||
@@ -89,7 +124,9 @@ public class FallbackSwipeHandler extends
|
||||
protected HomeAnimationFactory createHomeAnimationFactory(long duration) {
|
||||
mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
|
||||
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
|
||||
mContext.startActivity(new Intent(mGestureState.getHomeIntent()), options.toBundle());
|
||||
Intent intent = new Intent(mGestureState.getHomeIntent());
|
||||
mActiveAnimationFactory.addGestureContract(intent);
|
||||
mContext.startActivity(intent, options.toBundle());
|
||||
return mActiveAnimationFactory;
|
||||
}
|
||||
|
||||
@@ -130,15 +167,19 @@ public class FallbackSwipeHandler extends
|
||||
}
|
||||
|
||||
private class FallbackHomeAnimationFactory extends HomeAnimationFactory {
|
||||
|
||||
private final Rect mTempRect = new Rect();
|
||||
private final TransformParams mHomeAlphaParams = new TransformParams();
|
||||
private final AnimatedFloat mHomeAlpha;
|
||||
|
||||
private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat();
|
||||
|
||||
private final AnimatedFloat mRecentsAlpha = new AnimatedFloat();
|
||||
|
||||
private final RectF mTargetRect = new RectF();
|
||||
private SurfaceControl mSurfaceControl;
|
||||
|
||||
private final long mDuration;
|
||||
|
||||
private RectFSpringAnim mSpringAnim;
|
||||
FallbackHomeAnimationFactory(long duration) {
|
||||
mDuration = duration;
|
||||
|
||||
@@ -161,6 +202,15 @@ public class FallbackSwipeHandler extends
|
||||
this::updateRecentsActivityTransformDuringHomeAnim);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RectF getWindowTargetRect() {
|
||||
if (mTargetRect.isEmpty()) {
|
||||
mTargetRect.set(super.getWindowTargetRect());
|
||||
}
|
||||
return mTargetRect;
|
||||
}
|
||||
|
||||
private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
|
||||
RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withAlpha(mRecentsAlpha.value);
|
||||
@@ -217,5 +267,87 @@ public class FallbackSwipeHandler extends
|
||||
.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAnimation(RectFSpringAnim anim) {
|
||||
mSpringAnim = anim;
|
||||
}
|
||||
|
||||
private void onMessageReceived(Message msg) {
|
||||
try {
|
||||
Bundle data = msg.getData();
|
||||
RectF position = data.getParcelable(EXTRA_ICON_POSITION);
|
||||
if (!position.isEmpty()) {
|
||||
mSurfaceControl = data.getParcelable(EXTRA_ICON_SURFACE);
|
||||
mTargetRect.set(position);
|
||||
if (mSpringAnim != null) {
|
||||
mSpringAnim.onTargetPositionChanged();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(RectF currentRect, float progress, float radius) {
|
||||
if (mSurfaceControl != null) {
|
||||
currentRect.roundOut(mTempRect);
|
||||
Transaction t = new Transaction();
|
||||
t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0);
|
||||
t.apply();
|
||||
}
|
||||
}
|
||||
|
||||
private void addGestureContract(Intent intent) {
|
||||
if (mRunningOverHome || mGestureState.getRunningTask() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TaskKey key = new TaskKey(mGestureState.getRunningTask());
|
||||
if (key.getComponent() != null) {
|
||||
if (sMessageReceiver == null) {
|
||||
sMessageReceiver = new StaticMessageReceiver();
|
||||
}
|
||||
|
||||
Bundle gestureNavContract = new Bundle();
|
||||
gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent());
|
||||
gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId));
|
||||
gestureNavContract.putParcelable(EXTRA_REMOTE_CALLBACK,
|
||||
sMessageReceiver.newCallback(this::onMessageReceived));
|
||||
intent.putExtra(EXTRA_GESTURE_CONTRACT, gestureNavContract);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class StaticMessageReceiver implements Handler.Callback {
|
||||
|
||||
private final Messenger mMessenger =
|
||||
new Messenger(new Handler(Looper.getMainLooper(), this));
|
||||
|
||||
private ParcelUuid mCurrentUID = new ParcelUuid(UUID.randomUUID());
|
||||
private WeakReference<Consumer<Message>> mCurrentCallback = new WeakReference<>(null);
|
||||
|
||||
public Message newCallback(Consumer<Message> callback) {
|
||||
mCurrentUID = new ParcelUuid(UUID.randomUUID());
|
||||
mCurrentCallback = new WeakReference<>(callback);
|
||||
|
||||
Message msg = Message.obtain();
|
||||
msg.replyTo = mMessenger;
|
||||
msg.obj = mCurrentUID;
|
||||
return msg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(@NonNull Message message) {
|
||||
if (mCurrentUID.equals(message.obj)) {
|
||||
Consumer<Message> consumer = mCurrentCallback.get();
|
||||
if (consumer != null) {
|
||||
consumer.accept(message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,6 +110,13 @@ public abstract class BaseQuickstepLauncher extends Launcher
|
||||
.getHighResLoadingState().setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleGestureContract(Intent intent) {
|
||||
if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) {
|
||||
super.handleGestureContract(intent);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrimMemory(int level) {
|
||||
super.onTrimMemory(level);
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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.
|
||||
-->
|
||||
<com.android.launcher3.views.FloatingSurfaceView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
@@ -62,7 +62,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
TYPE_ALL_APPS_EDU,
|
||||
|
||||
TYPE_TASK_MENU,
|
||||
TYPE_OPTIONS_POPUP
|
||||
TYPE_OPTIONS_POPUP,
|
||||
TYPE_ICON_SURFACE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface FloatingViewType {}
|
||||
@@ -80,16 +81,18 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
// Popups related to quickstep UI
|
||||
public static final int TYPE_TASK_MENU = 1 << 10;
|
||||
public static final int TYPE_OPTIONS_POPUP = 1 << 11;
|
||||
public static final int TYPE_ICON_SURFACE = 1 << 12;
|
||||
|
||||
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
|
||||
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
|
||||
| TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
|
||||
| TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU;
|
||||
| TYPE_OPTIONS_POPUP | TYPE_SNACKBAR | TYPE_LISTENER | TYPE_ALL_APPS_EDU
|
||||
| TYPE_ICON_SURFACE;
|
||||
|
||||
// Type of popups which should be kept open during launcher rebind
|
||||
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
|
||||
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
|
||||
| TYPE_ALL_APPS_EDU;
|
||||
| TYPE_ALL_APPS_EDU | TYPE_ICON_SURFACE;
|
||||
|
||||
// Usually we show the back button when a floating view is open. Instead, hide for these types.
|
||||
public static final int TYPE_HIDE_BACK_BUTTON = TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
|
||||
|
||||
@@ -614,6 +614,9 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
@Override
|
||||
public void setIconVisible(boolean visible) {
|
||||
mIsIconVisible = visible;
|
||||
if (!mIsIconVisible) {
|
||||
resetIconScale();
|
||||
}
|
||||
Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
|
||||
applyCompoundDrawables(icon);
|
||||
}
|
||||
@@ -753,11 +756,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||
|
||||
@Override
|
||||
public SafeCloseable prepareDrawDragView() {
|
||||
if (getIcon() instanceof FastBitmapDrawable) {
|
||||
FastBitmapDrawable icon = (FastBitmapDrawable) getIcon();
|
||||
icon.setScale(1f);
|
||||
}
|
||||
resetIconScale();
|
||||
setForceHideDot(true);
|
||||
return () -> { };
|
||||
}
|
||||
|
||||
private void resetIconScale() {
|
||||
if (mIcon instanceof FastBitmapDrawable) {
|
||||
((FastBitmapDrawable) mIcon).setScale(1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static android.content.Intent.EXTRA_COMPONENT_NAME;
|
||||
import static android.content.Intent.EXTRA_USER;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Class to encapsulate the handshake protocol between Launcher and gestureNav.
|
||||
*/
|
||||
public class GestureNavContract {
|
||||
|
||||
private static final String TAG = "GestureNavContract";
|
||||
|
||||
public static final String EXTRA_GESTURE_CONTRACT = "gesture_nav_contract_v1";
|
||||
public static final String EXTRA_ICON_POSITION = "gesture_nav_contract_icon_position";
|
||||
public static final String EXTRA_ICON_SURFACE = "gesture_nav_contract_surface_control";
|
||||
public static final String EXTRA_REMOTE_CALLBACK = "android.intent.extra.REMOTE_CALLBACK";
|
||||
|
||||
public final ComponentName componentName;
|
||||
public final UserHandle user;
|
||||
|
||||
private final Message mCallback;
|
||||
|
||||
public GestureNavContract(ComponentName componentName, UserHandle user, Message callback) {
|
||||
this.componentName = componentName;
|
||||
this.user = user;
|
||||
this.mCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the position information to the receiver
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
public void sendEndPosition(RectF position, @Nullable SurfaceControl surfaceControl) {
|
||||
Bundle result = new Bundle();
|
||||
result.putParcelable(EXTRA_ICON_POSITION, position);
|
||||
result.putParcelable(EXTRA_ICON_SURFACE, surfaceControl);
|
||||
|
||||
Message callback = Message.obtain();
|
||||
callback.copyFrom(mCallback);
|
||||
callback.setData(result);
|
||||
|
||||
try {
|
||||
callback.replyTo.send(callback);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Error sending icon position", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears and returns the GestureNavContract if it was present in the intent.
|
||||
*/
|
||||
public static GestureNavContract fromIntent(Intent intent) {
|
||||
if (!Utilities.ATLEAST_R) {
|
||||
return null;
|
||||
}
|
||||
Bundle extras = intent.getBundleExtra(EXTRA_GESTURE_CONTRACT);
|
||||
if (extras == null) {
|
||||
return null;
|
||||
}
|
||||
intent.removeExtra(EXTRA_GESTURE_CONTRACT);
|
||||
|
||||
ComponentName componentName = extras.getParcelable(EXTRA_COMPONENT_NAME);
|
||||
UserHandle userHandle = extras.getParcelable(EXTRA_USER);
|
||||
Message callback = extras.getParcelable(EXTRA_REMOTE_CALLBACK);
|
||||
|
||||
if (componentName != null && userHandle != null && callback != null
|
||||
&& callback.replyTo != null) {
|
||||
return new GestureNavContract(componentName, userHandle, callback);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
|
||||
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
|
||||
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ICON_SURFACE;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE;
|
||||
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
|
||||
import static com.android.launcher3.InstallShortcutReceiver.FLAG_DRAG_AND_DROP;
|
||||
@@ -168,6 +169,7 @@ import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.launcher3.util.UiThreadHelper;
|
||||
import com.android.launcher3.util.ViewOnDrawExecutor;
|
||||
import com.android.launcher3.views.ActivityContext;
|
||||
import com.android.launcher3.views.FloatingSurfaceView;
|
||||
import com.android.launcher3.views.OptionsPopupView;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||
@@ -509,6 +511,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
||||
public void onEnterAnimationComplete() {
|
||||
super.onEnterAnimationComplete();
|
||||
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
|
||||
AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1450,6 +1453,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
||||
mLauncherCallbacks.onHomeIntent(internalStateHandled);
|
||||
}
|
||||
mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
|
||||
handleGestureContract(intent);
|
||||
} else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
|
||||
getStateManager().goToState(ALL_APPS, alreadyOnHome);
|
||||
}
|
||||
@@ -1457,6 +1461,17 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
||||
TraceHelper.INSTANCE.endSection(traceToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles gesture nav contract
|
||||
*/
|
||||
protected void handleGestureContract(Intent intent) {
|
||||
GestureNavContract gnc = GestureNavContract.fromIntent(intent);
|
||||
if (gnc != null) {
|
||||
AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE);
|
||||
FloatingSurfaceView.show(this, gnc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hides the keyboard if visible
|
||||
*/
|
||||
|
||||
@@ -196,13 +196,18 @@ public class FloatingIconView extends FrameLayout implements
|
||||
layout(left, lp.topMargin, left + lp.width, lp.topMargin + lp.height);
|
||||
}
|
||||
|
||||
private static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
|
||||
RectF outRect) {
|
||||
getLocationBoundsForView(launcher, v, isOpening, outRect, new Rect());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the location bounds of a view and returns the overall rotation.
|
||||
* - For DeepShortcutView, we return the bounds of the icon view.
|
||||
* - For BubbleTextView, we return the icon bounds.
|
||||
*/
|
||||
private static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
|
||||
RectF outRect) {
|
||||
public static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
|
||||
RectF outRect, Rect outViewBounds) {
|
||||
boolean ignoreTransform = !isOpening;
|
||||
if (v instanceof DeepShortcutView) {
|
||||
v = ((DeepShortcutView) v).getBubbleText();
|
||||
@@ -215,17 +220,16 @@ public class FloatingIconView extends FrameLayout implements
|
||||
return;
|
||||
}
|
||||
|
||||
Rect iconBounds = new Rect();
|
||||
if (v instanceof BubbleTextView) {
|
||||
((BubbleTextView) v).getIconBounds(iconBounds);
|
||||
((BubbleTextView) v).getIconBounds(outViewBounds);
|
||||
} else if (v instanceof FolderIcon) {
|
||||
((FolderIcon) v).getPreviewBounds(iconBounds);
|
||||
((FolderIcon) v).getPreviewBounds(outViewBounds);
|
||||
} else {
|
||||
iconBounds.set(0, 0, v.getWidth(), v.getHeight());
|
||||
outViewBounds.set(0, 0, v.getWidth(), v.getHeight());
|
||||
}
|
||||
|
||||
float[] points = new float[] {iconBounds.left, iconBounds.top, iconBounds.right,
|
||||
iconBounds.bottom};
|
||||
float[] points = new float[] {outViewBounds.left, outViewBounds.top, outViewBounds.right,
|
||||
outViewBounds.bottom};
|
||||
Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points,
|
||||
false, ignoreTransform);
|
||||
outRect.set(
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* 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.views;
|
||||
|
||||
import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView;
|
||||
import static com.android.launcher3.views.IconLabelDotView.setIconAndDotVisible;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Picture;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.GestureNavContract;
|
||||
import com.android.launcher3.Insettable;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.util.DefaultDisplay;
|
||||
import com.android.launcher3.util.Executors;
|
||||
|
||||
/**
|
||||
* Similar to {@link FloatingIconView} but displays a surface with the targetIcon. It then passes
|
||||
* the surfaceHandle to the {@link GestureNavContract}.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
public class FloatingSurfaceView extends AbstractFloatingView implements
|
||||
OnGlobalLayoutListener, Insettable, SurfaceHolder.Callback2 {
|
||||
|
||||
private final RectF mTmpPosition = new RectF();
|
||||
|
||||
private final Launcher mLauncher;
|
||||
private final RectF mIconPosition = new RectF();
|
||||
|
||||
private final Rect mIconBounds = new Rect();
|
||||
private final Picture mPicture = new Picture();
|
||||
private final Runnable mRemoveViewRunnable = this::removeViewFromParent;
|
||||
|
||||
private final SurfaceView mSurfaceView;
|
||||
|
||||
|
||||
private View mIcon;
|
||||
private GestureNavContract mContract;
|
||||
|
||||
public FloatingSurfaceView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public FloatingSurfaceView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public FloatingSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
|
||||
mSurfaceView = new SurfaceView(context);
|
||||
mSurfaceView.setZOrderOnTop(true);
|
||||
|
||||
mSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
|
||||
mSurfaceView.getHolder().addCallback(this);
|
||||
mIsOpen = true;
|
||||
addView(mSurfaceView);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void handleClose(boolean animate) {
|
||||
setCurrentIconVisible(true);
|
||||
mLauncher.getViewCache().recycleView(R.layout.floating_surface_view, this);
|
||||
mContract = null;
|
||||
mIcon = null;
|
||||
mIsOpen = false;
|
||||
|
||||
// Remove after some time, to avoid flickering
|
||||
Executors.MAIN_EXECUTOR.getHandler().postDelayed(mRemoveViewRunnable,
|
||||
DefaultDisplay.INSTANCE.get(mLauncher).getInfo().singleFrameMs);
|
||||
}
|
||||
|
||||
private void removeViewFromParent() {
|
||||
mPicture.beginRecording(1, 1);
|
||||
mPicture.endRecording();
|
||||
mLauncher.getDragLayer().removeView(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the surfaceView for the provided contract
|
||||
*/
|
||||
public static void show(Launcher launcher, GestureNavContract contract) {
|
||||
FloatingSurfaceView view = launcher.getViewCache().getView(R.layout.floating_surface_view,
|
||||
launcher, launcher.getDragLayer());
|
||||
view.mContract = contract;
|
||||
view.mIsOpen = true;
|
||||
|
||||
// Cancel any pending remove
|
||||
Executors.MAIN_EXECUTOR.getHandler().removeCallbacks(view.mRemoveViewRunnable);
|
||||
view.removeViewFromParent();
|
||||
launcher.getDragLayer().addView(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logActionCommand(int command) { }
|
||||
|
||||
@Override
|
||||
protected boolean isOfType(int type) {
|
||||
return (type & TYPE_ICON_SURFACE) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
|
||||
close(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(this);
|
||||
updateIconLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
setCurrentIconVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
updateIconLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) { }
|
||||
|
||||
private void updateIconLocation() {
|
||||
if (mContract == null) {
|
||||
return;
|
||||
}
|
||||
View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(
|
||||
mContract.componentName.getPackageName(), mContract.user);
|
||||
|
||||
boolean iconChanged = mIcon != icon;
|
||||
if (iconChanged) {
|
||||
setCurrentIconVisible(true);
|
||||
mIcon = icon;
|
||||
setCurrentIconVisible(false);
|
||||
}
|
||||
|
||||
if (icon != null && icon.isAttachedToWindow()) {
|
||||
getLocationBoundsForView(mLauncher, icon, false, mTmpPosition, mIconBounds);
|
||||
|
||||
if (!mTmpPosition.equals(mIconPosition)) {
|
||||
mIconPosition.set(mTmpPosition);
|
||||
sendIconInfo();
|
||||
|
||||
LayoutParams lp = (LayoutParams) mSurfaceView.getLayoutParams();
|
||||
lp.width = Math.round(mIconPosition.width());
|
||||
lp.height = Math.round(mIconPosition.height());
|
||||
lp.leftMargin = Math.round(mIconPosition.left);
|
||||
lp.topMargin = Math.round(mIconPosition.top);
|
||||
}
|
||||
}
|
||||
if (iconChanged && !mIconBounds.isEmpty()) {
|
||||
// Record the icon display
|
||||
setCurrentIconVisible(true);
|
||||
Canvas c = mPicture.beginRecording(mIconBounds.width(), mIconBounds.height());
|
||||
c.translate(-mIconBounds.left, -mIconBounds.top);
|
||||
mIcon.draw(c);
|
||||
mPicture.endRecording();
|
||||
setCurrentIconVisible(false);
|
||||
drawOnSurface();
|
||||
}
|
||||
}
|
||||
|
||||
private void sendIconInfo() {
|
||||
if (mContract != null && !mIconPosition.isEmpty()) {
|
||||
mContract.sendEndPosition(mIconPosition, mSurfaceView.getSurfaceControl());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
|
||||
drawOnSurface();
|
||||
sendIconInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder,
|
||||
int format, int width, int height) {
|
||||
drawOnSurface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {}
|
||||
|
||||
@Override
|
||||
public void surfaceRedrawNeeded(@NonNull SurfaceHolder surfaceHolder) {
|
||||
drawOnSurface();
|
||||
}
|
||||
|
||||
private void drawOnSurface() {
|
||||
SurfaceHolder surfaceHolder = mSurfaceView.getHolder();
|
||||
|
||||
Canvas c = surfaceHolder.lockHardwareCanvas();
|
||||
if (c != null) {
|
||||
mPicture.draw(c);
|
||||
surfaceHolder.unlockCanvasAndPost(c);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurrentIconVisible(boolean isVisible) {
|
||||
if (mIcon != null) {
|
||||
setIconAndDotVisible(mIcon, isVisible);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user