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;
|
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.launcher3.anim.Interpolators.ACCEL;
|
||||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
|
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
|
||||||
|
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.app.ActivityOptions;
|
import android.app.ActivityOptions;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Matrix;
|
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;
|
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.PendingAnimation;
|
||||||
import com.android.launcher3.anim.SpringAnimationBuilder;
|
import com.android.launcher3.anim.SpringAnimationBuilder;
|
||||||
import com.android.quickstep.fallback.FallbackRecentsView;
|
import com.android.quickstep.fallback.FallbackRecentsView;
|
||||||
|
import com.android.quickstep.util.RectFSpringAnim;
|
||||||
import com.android.quickstep.util.TransformParams;
|
import com.android.quickstep.util.TransformParams;
|
||||||
import com.android.quickstep.util.TransformParams.BuilderProxy;
|
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.ActivityManagerWrapper;
|
||||||
import com.android.systemui.shared.system.InputConsumerController;
|
import com.android.systemui.shared.system.InputConsumerController;
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
|
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.
|
* Handles the navigation gestures when a 3rd party launcher is the default home activity.
|
||||||
*/
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.R)
|
||||||
public class FallbackSwipeHandler extends
|
public class FallbackSwipeHandler extends
|
||||||
BaseSwipeUpHandlerV2<RecentsActivity, FallbackRecentsView> {
|
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 FallbackHomeAnimationFactory mActiveAnimationFactory;
|
||||||
private final boolean mRunningOverHome;
|
private final boolean mRunningOverHome;
|
||||||
|
|
||||||
@@ -89,7 +124,9 @@ public class FallbackSwipeHandler extends
|
|||||||
protected HomeAnimationFactory createHomeAnimationFactory(long duration) {
|
protected HomeAnimationFactory createHomeAnimationFactory(long duration) {
|
||||||
mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
|
mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration);
|
||||||
ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0);
|
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;
|
return mActiveAnimationFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,15 +167,19 @@ public class FallbackSwipeHandler extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class FallbackHomeAnimationFactory extends HomeAnimationFactory {
|
private class FallbackHomeAnimationFactory extends HomeAnimationFactory {
|
||||||
|
private final Rect mTempRect = new Rect();
|
||||||
private final TransformParams mHomeAlphaParams = new TransformParams();
|
private final TransformParams mHomeAlphaParams = new TransformParams();
|
||||||
private final AnimatedFloat mHomeAlpha;
|
private final AnimatedFloat mHomeAlpha;
|
||||||
|
|
||||||
private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat();
|
private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat();
|
||||||
|
|
||||||
private final AnimatedFloat mRecentsAlpha = new AnimatedFloat();
|
private final AnimatedFloat mRecentsAlpha = new AnimatedFloat();
|
||||||
|
|
||||||
|
private final RectF mTargetRect = new RectF();
|
||||||
|
private SurfaceControl mSurfaceControl;
|
||||||
|
|
||||||
private final long mDuration;
|
private final long mDuration;
|
||||||
|
|
||||||
|
private RectFSpringAnim mSpringAnim;
|
||||||
FallbackHomeAnimationFactory(long duration) {
|
FallbackHomeAnimationFactory(long duration) {
|
||||||
mDuration = duration;
|
mDuration = duration;
|
||||||
|
|
||||||
@@ -161,6 +202,15 @@ public class FallbackSwipeHandler extends
|
|||||||
this::updateRecentsActivityTransformDuringHomeAnim);
|
this::updateRecentsActivityTransformDuringHomeAnim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public RectF getWindowTargetRect() {
|
||||||
|
if (mTargetRect.isEmpty()) {
|
||||||
|
mTargetRect.set(super.getWindowTargetRect());
|
||||||
|
}
|
||||||
|
return mTargetRect;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
|
private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder,
|
||||||
RemoteAnimationTargetCompat app, TransformParams params) {
|
RemoteAnimationTargetCompat app, TransformParams params) {
|
||||||
builder.withAlpha(mRecentsAlpha.value);
|
builder.withAlpha(mRecentsAlpha.value);
|
||||||
@@ -217,5 +267,87 @@ public class FallbackSwipeHandler extends
|
|||||||
.start();
|
.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);
|
.getHighResLoadingState().setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handleGestureContract(Intent intent) {
|
||||||
|
if (FeatureFlags.SEPARATE_RECENTS_ACTIVITY.get()) {
|
||||||
|
super.handleGestureContract(intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTrimMemory(int level) {
|
public void onTrimMemory(int level) {
|
||||||
super.onTrimMemory(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_ALL_APPS_EDU,
|
||||||
|
|
||||||
TYPE_TASK_MENU,
|
TYPE_TASK_MENU,
|
||||||
TYPE_OPTIONS_POPUP
|
TYPE_OPTIONS_POPUP,
|
||||||
|
TYPE_ICON_SURFACE
|
||||||
})
|
})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface FloatingViewType {}
|
public @interface FloatingViewType {}
|
||||||
@@ -80,16 +81,18 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
|||||||
// Popups related to quickstep UI
|
// Popups related to quickstep UI
|
||||||
public static final int TYPE_TASK_MENU = 1 << 10;
|
public static final int TYPE_TASK_MENU = 1 << 10;
|
||||||
public static final int TYPE_OPTIONS_POPUP = 1 << 11;
|
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
|
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
|
||||||
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
|
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
|
||||||
| TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE | TYPE_TASK_MENU
|
| 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
|
// Type of popups which should be kept open during launcher rebind
|
||||||
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
|
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
|
||||||
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_ON_BOARD_POPUP | TYPE_DISCOVERY_BOUNCE
|
| 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.
|
// 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
|
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
|
@Override
|
||||||
public void setIconVisible(boolean visible) {
|
public void setIconVisible(boolean visible) {
|
||||||
mIsIconVisible = visible;
|
mIsIconVisible = visible;
|
||||||
|
if (!mIsIconVisible) {
|
||||||
|
resetIconScale();
|
||||||
|
}
|
||||||
Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
|
Drawable icon = visible ? mIcon : new ColorDrawable(Color.TRANSPARENT);
|
||||||
applyCompoundDrawables(icon);
|
applyCompoundDrawables(icon);
|
||||||
}
|
}
|
||||||
@@ -753,11 +756,14 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SafeCloseable prepareDrawDragView() {
|
public SafeCloseable prepareDrawDragView() {
|
||||||
if (getIcon() instanceof FastBitmapDrawable) {
|
resetIconScale();
|
||||||
FastBitmapDrawable icon = (FastBitmapDrawable) getIcon();
|
|
||||||
icon.setScale(1f);
|
|
||||||
}
|
|
||||||
setForceHideDot(true);
|
setForceHideDot(true);
|
||||||
return () -> { };
|
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 android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
|
||||||
|
|
||||||
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
|
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_REBIND_SAFE;
|
||||||
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
|
import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR;
|
||||||
import static com.android.launcher3.InstallShortcutReceiver.FLAG_DRAG_AND_DROP;
|
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.UiThreadHelper;
|
||||||
import com.android.launcher3.util.ViewOnDrawExecutor;
|
import com.android.launcher3.util.ViewOnDrawExecutor;
|
||||||
import com.android.launcher3.views.ActivityContext;
|
import com.android.launcher3.views.ActivityContext;
|
||||||
|
import com.android.launcher3.views.FloatingSurfaceView;
|
||||||
import com.android.launcher3.views.OptionsPopupView;
|
import com.android.launcher3.views.OptionsPopupView;
|
||||||
import com.android.launcher3.views.ScrimView;
|
import com.android.launcher3.views.ScrimView;
|
||||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||||
@@ -509,6 +511,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||||||
public void onEnterAnimationComplete() {
|
public void onEnterAnimationComplete() {
|
||||||
super.onEnterAnimationComplete();
|
super.onEnterAnimationComplete();
|
||||||
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
|
mRotationHelper.setCurrentTransitionRequest(REQUEST_NONE);
|
||||||
|
AbstractFloatingView.closeOpenViews(this, false, TYPE_ICON_SURFACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1450,6 +1453,7 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||||||
mLauncherCallbacks.onHomeIntent(internalStateHandled);
|
mLauncherCallbacks.onHomeIntent(internalStateHandled);
|
||||||
}
|
}
|
||||||
mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
|
mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
|
||||||
|
handleGestureContract(intent);
|
||||||
} else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
|
} else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
|
||||||
getStateManager().goToState(ALL_APPS, alreadyOnHome);
|
getStateManager().goToState(ALL_APPS, alreadyOnHome);
|
||||||
}
|
}
|
||||||
@@ -1457,6 +1461,17 @@ public class Launcher extends StatefulActivity<LauncherState> implements Launche
|
|||||||
TraceHelper.INSTANCE.endSection(traceToken);
|
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
|
* 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);
|
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.
|
* Gets the location bounds of a view and returns the overall rotation.
|
||||||
* - For DeepShortcutView, we return the bounds of the icon view.
|
* - For DeepShortcutView, we return the bounds of the icon view.
|
||||||
* - For BubbleTextView, we return the icon bounds.
|
* - For BubbleTextView, we return the icon bounds.
|
||||||
*/
|
*/
|
||||||
private static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
|
public static void getLocationBoundsForView(Launcher launcher, View v, boolean isOpening,
|
||||||
RectF outRect) {
|
RectF outRect, Rect outViewBounds) {
|
||||||
boolean ignoreTransform = !isOpening;
|
boolean ignoreTransform = !isOpening;
|
||||||
if (v instanceof DeepShortcutView) {
|
if (v instanceof DeepShortcutView) {
|
||||||
v = ((DeepShortcutView) v).getBubbleText();
|
v = ((DeepShortcutView) v).getBubbleText();
|
||||||
@@ -215,17 +220,16 @@ public class FloatingIconView extends FrameLayout implements
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect iconBounds = new Rect();
|
|
||||||
if (v instanceof BubbleTextView) {
|
if (v instanceof BubbleTextView) {
|
||||||
((BubbleTextView) v).getIconBounds(iconBounds);
|
((BubbleTextView) v).getIconBounds(outViewBounds);
|
||||||
} else if (v instanceof FolderIcon) {
|
} else if (v instanceof FolderIcon) {
|
||||||
((FolderIcon) v).getPreviewBounds(iconBounds);
|
((FolderIcon) v).getPreviewBounds(outViewBounds);
|
||||||
} else {
|
} 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,
|
float[] points = new float[] {outViewBounds.left, outViewBounds.top, outViewBounds.right,
|
||||||
iconBounds.bottom};
|
outViewBounds.bottom};
|
||||||
Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points,
|
Utilities.getDescendantCoordRelativeToAncestor(v, launcher.getDragLayer(), points,
|
||||||
false, ignoreTransform);
|
false, ignoreTransform);
|
||||||
outRect.set(
|
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