Merge "Show split select instructions toast when starting split on workspace" into udc-qpr-dev am: b9a5c4551f

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/24062180

Change-Id: Ifac48dc2f1cbf06506ec4c59003e2224aba060ee
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Vinit Nayak
2023-07-17 22:35:37 +00:00
committed by Automerger Merge Worker
6 changed files with 130 additions and 27 deletions

View File

@@ -24,12 +24,15 @@
android:paddingTop="@dimen/split_instructions_vertical_padding"
android:paddingBottom="@dimen/split_instructions_vertical_padding"
android:elevation="@dimen/split_instructions_elevation"
android:visibility="gone">
android:visibility="gone"
android:importantForAccessibility="yes">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/split_instructions_text"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:gravity="center"
android:textColor="?androidprv:attr/textColorOnAccent"
android:drawableEnd="@drawable/ic_split_horizontal"
android:drawablePadding="@dimen/split_instructions_drawable_padding"
android:text="@string/toast_split_select_app" />
</com.android.quickstep.views.SplitInstructionsView>

View File

@@ -230,6 +230,7 @@
<string name="action_split">Split</string>
<!-- Label for toast with instructions for split screen selection mode. [CHAR_LIMIT=50] -->
<string name="toast_split_select_app">Tap another app to use split screen</string>
<string name="toast_split_select_cont_desc">Exit split screen selection</string>
<!-- Label for toast when app selected for split isn't supported. [CHAR_LIMIT=50] -->
<string name="toast_split_app_unsupported">Choose another app to use split screen</string>
<!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->

View File

@@ -544,17 +544,9 @@ public class QuickstepLauncher extends Launcher {
ArrayList<TouchController> list = new ArrayList<>();
list.add(getDragController());
Consumer<AnimatorSet> splitAnimator = animatorSet -> {
AnimatorSet anim = mSplitSelectStateController.getSplitAnimationController()
.createPlaceholderDismissAnim(QuickstepLauncher.this);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mSplitSelectStateController.resetState();
}
});
animatorSet.play(anim);
};
Consumer<AnimatorSet> splitAnimator = animatorSet ->
animatorSet.play(mSplitSelectStateController.getSplitAnimationController()
.createPlaceholderDismissAnim(this));
switch (mode) {
case NO_BUTTON:
list.add(new NoButtonQuickSwitchTouchController(this));
@@ -673,6 +665,8 @@ public class QuickstepLauncher extends Launcher {
mSplitSelectStateController.resetState();
}
});
anim.add(mSplitSelectStateController.getSplitAnimationController()
.getShowSplitInstructionsAnim(this).buildAnim());
anim.buildAnim().start();
}

View File

@@ -26,15 +26,17 @@ import android.graphics.Rect
import android.graphics.RectF
import android.graphics.drawable.Drawable
import android.view.View
import com.android.app.animation.Interpolators
import com.android.launcher3.DeviceProfile
import com.android.launcher3.Launcher
import com.android.launcher3.Utilities
import com.android.launcher3.anim.PendingAnimation
import com.android.launcher3.dragndrop.DragLayer
import com.android.launcher3.statemanager.StatefulActivity
import com.android.launcher3.util.SplitConfigurationOptions.SplitSelectSource
import com.android.launcher3.views.BaseDragLayer
import com.android.quickstep.views.FloatingTaskView
import com.android.quickstep.views.IconView
import com.android.quickstep.views.RecentsView
import com.android.quickstep.views.SplitInstructionsView
import com.android.quickstep.views.TaskThumbnailView
import com.android.quickstep.views.TaskView
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
@@ -58,6 +60,8 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
)
}
var splitInstructionsView: SplitInstructionsView? = null
/**
* Returns different elements to animate for the initial split selection animation
* depending on the state of the surface from which the split was initiated
@@ -188,31 +192,26 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
}
/** Does not play any animation if user is not currently in split selection state. */
fun playPlaceholderDismissAnim(launcher: Launcher) {
fun playPlaceholderDismissAnim(launcher: StatefulActivity<*>) {
if (!splitSelectStateController.isSplitSelectActive) {
return
}
val anim = createPlaceholderDismissAnim(launcher)
anim.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
splitSelectStateController.resetState()
}
})
anim.start()
}
/** Returns [AnimatorSet] which slides initial split placeholder view offscreen. */
fun createPlaceholderDismissAnim(launcher: Launcher) : AnimatorSet {
fun createPlaceholderDismissAnim(launcher: StatefulActivity<*>) : AnimatorSet {
val animatorSet = AnimatorSet()
val recentsView : RecentsView<*, *> = launcher.getOverviewPanel()
val floatingTask: FloatingTaskView = splitSelectStateController.firstFloatingTaskView
?: return animatorSet
// We are in split selection state currently, transitioning to another state
val dragLayer: DragLayer = launcher.dragLayer
val dragLayer: BaseDragLayer<*> = launcher.dragLayer
val onScreenRectF = RectF()
Utilities.getBoundsForViewInDragLayer(launcher.dragLayer, floatingTask,
Utilities.getBoundsForViewInDragLayer(dragLayer, floatingTask,
Rect(0, 0, floatingTask.width, floatingTask.height),
false, null, onScreenRectF)
// Get the part of the floatingTask that intersects with the DragLayer (i.e. the
@@ -233,6 +232,42 @@ class SplitAnimationController(val splitSelectStateController: SplitSelectStateC
floatingTask.stagePosition,
launcher.deviceProfile
)))
animatorSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
splitSelectStateController.resetState()
safeRemoveViewFromDragLayer(launcher, splitInstructionsView)
}
})
return animatorSet
}
/**
* Returns a [PendingAnimation] to animate in the chip to instruct a user to select a second
* app for splitscreen
*/
fun getShowSplitInstructionsAnim(launcher: StatefulActivity<*>) : PendingAnimation {
safeRemoveViewFromDragLayer(launcher, splitInstructionsView)
splitInstructionsView = SplitInstructionsView.getSplitInstructionsView(launcher)
val timings = AnimUtils.getDeviceOverviewToSplitTimings(launcher.deviceProfile.isTablet)
val anim = PendingAnimation(100 /*duration */)
anim.setViewAlpha(splitInstructionsView, 1f,
Interpolators.clampToProgress(Interpolators.LINEAR,
timings.instructionsContainerFadeInStartOffset,
timings.instructionsContainerFadeInEndOffset))
anim.setViewAlpha(splitInstructionsView!!.textView, 1f,
Interpolators.clampToProgress(Interpolators.LINEAR,
timings.instructionsTextFadeInStartOffset,
timings.instructionsTextFadeInEndOffset))
anim.addFloat(splitInstructionsView, SplitInstructionsView.UNFOLD, 0.1f, 1f,
Interpolators.clampToProgress(Interpolators.EMPHASIZED_DECELERATE,
timings.instructionsUnfoldStartOffset,
timings.instructionsUnfoldEndOffset))
return anim
}
private fun safeRemoveViewFromDragLayer(launcher: StatefulActivity<*>, view: View?) {
if (view != null) {
launcher.dragLayer.removeView(view)
}
}
}

View File

@@ -17,15 +17,21 @@
package com.android.quickstep.views;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StatefulActivity;
/**
@@ -65,7 +71,7 @@ public class SplitInstructionsView extends FrameLayout {
mLauncher = (StatefulActivity) context;
}
static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) {
public static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) {
ViewGroup dragLayer = launcher.getDragLayer();
final SplitInstructionsView splitInstructionsView =
(SplitInstructionsView) launcher.getLayoutInflater().inflate(
@@ -73,9 +79,7 @@ public class SplitInstructionsView extends FrameLayout {
dragLayer,
false
);
splitInstructionsView.mTextView = splitInstructionsView.findViewById(
R.id.split_instructions_text);
splitInstructionsView.init();
// Since textview overlays base view, and we sometimes manipulate the alpha of each
// simultaneously, force overlapping rendering to false prevents redrawing of pixels,
@@ -92,6 +96,71 @@ public class SplitInstructionsView extends FrameLayout {
ensureProperRotation();
}
private void init() {
mTextView = findViewById(R.id.split_instructions_text);
if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
mTextView.setCompoundDrawables(null, null, null, null);
return;
}
mTextView.setOnTouchListener((v, event) -> {
if (isTouchInsideRightCompoundDrawable(event)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
exitSplitSelection();
}
return true;
}
return false;
});
}
private void exitSplitSelection() {
((RecentsView) mLauncher.getOverviewPanel()).getSplitSelectController()
.getSplitAnimationController().playPlaceholderDismissAnim(mLauncher);
mLauncher.getStateManager().goToState(LauncherState.NORMAL);
}
private boolean isTouchInsideRightCompoundDrawable(MotionEvent event) {
// Get the right compound drawable of the TextView.
Drawable rightDrawable = mTextView.getCompoundDrawablesRelative()[2];
// Check if the touch event intersects with the drawable's bounds.
if (rightDrawable != null) {
// We can get away w/o caring about the Y bounds since it's such a small view, if it's
// above/below the drawable just assume they meant to touch it. ¯\_(ツ)_/¯
return event.getX() >= (mTextView.getWidth() - rightDrawable.getBounds().width());
} else {
return false;
}
}
@Override
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
return;
}
info.addAction(new AccessibilityNodeInfo.AccessibilityAction(
R.string.toast_split_select_cont_desc,
getResources().getString(R.string.toast_split_select_cont_desc)
));
}
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (!FeatureFlags.ENABLE_SPLIT_FROM_WORKSPACE_TO_WORKSPACE.get()) {
return super.performAccessibilityAction(action, arguments);
}
if (action == R.string.toast_split_select_cont_desc) {
exitSplitSelection();
return true;
}
return super.performAccessibilityAction(action, arguments);
}
void ensureProperRotation() {
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler()
.setSplitInstructionsParams(

View File

@@ -411,6 +411,7 @@
<dimen name="split_instructions_elevation">1dp</dimen>
<dimen name="split_instructions_horizontal_padding">24dp</dimen>
<dimen name="split_instructions_vertical_padding">12dp</dimen>
<dimen name="split_instructions_drawable_padding">10dp</dimen>
<dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen>
<dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>