mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-19 02:38:20 +00:00
Merge "Show split select instructions toast when starting split on workspace" into udc-qpr-dev
This commit is contained in:
@@ -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>
|
||||
@@ -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] -->
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user