Implement non-disappearing View for split staging instructions am: b0cce86385

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

Change-Id: I9528d19875838af2cd6df93c503ac2275e66187f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Jeremy Sim
2022-05-23 22:24:38 +00:00
committed by Automerger Merge Worker
9 changed files with 317 additions and 3 deletions
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid android:color="?androidprv:attr/colorAccentPrimary" />
<corners android:radius="@dimen/split_instructions_radius" />
</shape>
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2022 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.quickstep.views.SplitInstructionsView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/split_instructions_background"
android:paddingRight="@dimen/split_instructions_horizontal_padding"
android:paddingLeft="@dimen/split_instructions_horizontal_padding"
android:paddingTop="@dimen/split_instructions_vertical_padding"
android:paddingBottom="@dimen/split_instructions_vertical_padding"
android:elevation="@dimen/split_instructions_elevation"
android:visibility="gone">
<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:text="@string/toast_split_select_app" />
</com.android.quickstep.views.SplitInstructionsView>
@@ -630,6 +630,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
private SplitInstructionsView mSplitInstructionsView;
@Nullable
private QuickstepSystemShortcut.SplitSelectSource mSplitSelectSource;
@@ -2764,11 +2766,15 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
mFirstFloatingTaskView.addAnimation(anim, startingTaskRect, mTempRect,
false /* fadeWithThumbnail */, true /* isStagedTask */);
}
mSplitInstructionsView = SplitInstructionsView.getSplitInstructionsView(mActivity);
mSplitInstructionsView.setAlpha(0);
anim.addFloat(mSplitInstructionsView, SplitInstructionsView.ALPHA_FLOAT, 0, 1, ACCEL);
InteractionJankMonitorWrapper.begin(this,
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER, "First tile selected");
anim.addEndListener(success -> {
if (success) {
mSplitToast.show();
InteractionJankMonitorWrapper.end(
InteractionJankMonitorWrapper.CUJ_SPLIT_SCREEN_ENTER);
} else {
@@ -4099,6 +4105,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
@SuppressLint("WrongCall")
protected void resetFromSplitSelectionState() {
if (mSplitSelectSource != null || mSplitHiddenTaskViewIndex != -1) {
if (mSplitInstructionsView != null) {
mActivity.getDragLayer().removeView(mSplitInstructionsView);
mSplitInstructionsView = null;
}
if (mFirstFloatingTaskView != null) {
mActivity.getRootView().removeView(mFirstFloatingTaskView);
mFirstFloatingTaskView = null;
@@ -4164,6 +4174,10 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
taskViewsFloat.first.set(this, getSplitSelectTranslation());
taskViewsFloat.second.set(this, 0f);
if (mSplitInstructionsView != null) {
mSplitInstructionsView.ensureProperRotation();
}
applySplitPrimaryScrollOffset();
}
@@ -0,0 +1,123 @@
/*
* Copyright 2022 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.quickstep.views;
import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
import android.content.Context;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.util.DisplayController;
/**
* A rounded rectangular component containing a single TextView.
* Appears when a split is in progress, and tells the user to select a second app to initiate
* splitscreen.
*
* Appears and disappears concurrently with a FloatingTaskView.
*/
public class SplitInstructionsView extends FrameLayout {
private final StatefulActivity mLauncher;
public static final FloatProperty<SplitInstructionsView> ALPHA_FLOAT =
new FloatProperty<SplitInstructionsView>("SplitInstructionsAlpha") {
@Override
public void setValue(SplitInstructionsView splitInstructionsView, float v) {
splitInstructionsView.setVisibility(v != 0 ? VISIBLE : GONE);
splitInstructionsView.setAlpha(v);
}
@Override
public Float get(SplitInstructionsView splitInstructionsView) {
return splitInstructionsView.getAlpha();
}
};
public SplitInstructionsView(Context context) {
this(context, null);
}
public SplitInstructionsView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SplitInstructionsView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mLauncher = (StatefulActivity) context;
}
static SplitInstructionsView getSplitInstructionsView(StatefulActivity launcher) {
ViewGroup dragLayer = launcher.getDragLayer();
final SplitInstructionsView splitInstructionsView =
(SplitInstructionsView) launcher.getLayoutInflater().inflate(
R.layout.split_instructions_view,
dragLayer,
false
);
dragLayer.addView(splitInstructionsView);
return splitInstructionsView;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ensureProperRotation();
}
void ensureProperRotation() {
((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler()
.setSplitInstructionsParams(
this,
mLauncher.getDeviceProfile(),
getMeasuredHeight(),
getMeasuredWidth(),
getThreeButtonNavShift()
);
}
// In some cases, when user is using 3-button nav, there isn't enough room for both the
// 3-button nav and a centered SplitInstructionsView. This function will return an int that will
// be used to shift the SplitInstructionsView over a bit so that everything looks well-spaced.
// In many cases, this will return 0, since we don't need to shift it away from the center.
int getThreeButtonNavShift() {
DeviceProfile dp = mLauncher.getDeviceProfile();
if ((DisplayController.getNavigationMode(getContext()) == THREE_BUTTONS)
&& ((dp.isTwoPanels) || (dp.isTablet && !dp.isLandscape))) {
int navButtonWidth = getResources().getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_size);
int extraMargin = getResources().getDimensionPixelSize(
R.dimen.taskbar_contextual_button_margin);
// Explanation: The 3-button nav for non-phones sits on one side of the screen, taking
// up 3 buttons + a side margin worth of space. Our splitInstructionsView starts in the
// center of the screen and we want to center it in the remaining space, therefore we
// want to shift it over by half the 3-button layout's width.
// If the user is using an RtL layout, we shift it the opposite way.
return -((3 * navButtonWidth + extraMargin) / 2) * (isLayoutRtl() ? -1 : 1);
} else {
return 0;
}
}
}
+11 -2
View File
@@ -390,8 +390,17 @@
<dimen name="split_placeholder_inset">16dp</dimen>
<dimen name="split_placeholder_icon_size">44dp</dimen>
<dimen name="task_menu_width_grid">216dp</dimen>
<dimen name="split_instructions_radius">22dp</dimen>
<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_bottom_margin_tablet_landscape">32dp</dimen>
<dimen name="split_instructions_bottom_margin_tablet_portrait">44dp</dimen>
<dimen name="split_instructions_bottom_margin_twopanels_landscape">33dp</dimen>
<dimen name="split_instructions_bottom_margin_twopanels_portrait">51dp</dimen>
<dimen name="split_instructions_bottom_margin_phone_landscape">24dp</dimen>
<dimen name="split_instructions_bottom_margin_phone_portrait">60dp</dimen>
<!-- Workspace grid visualization parameters -->
<dimen name="grid_visualization_rounding_radius">28dp</dimen>
<dimen name="grid_visualization_horizontal_cell_spacing">6dp</dimen>
@@ -19,6 +19,7 @@ package com.android.launcher3.touch;
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.Gravity.END;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
@@ -424,6 +425,28 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
- 1.0f * drawableHeight / 2));
}
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
out.setPivotY(0);
out.setRotation(getDegreesRotated());
int distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
// Adjust for any insets on the left edge
int insetCorrectionX = dp.getInsets().left;
// Center the view in case of unbalanced insets on top or bottom of screen
int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
out.setTranslationX(splitInstructionsHeight + distanceToEdge - insetCorrectionX);
out.setTranslationY(((splitInstructionsHeight - splitInstructionsWidth) / 2f)
+ insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
// Setting gravity to LEFT instead of the lint-recommended START because we always want this
// view to be screen-left when phone is in landscape, regardless of the RtL setting.
lp.gravity = LEFT | CENTER_VERTICAL;
out.setLayoutParams(lp);
}
@Override
public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
@StagePosition int stagePosition, Rect out1, Rect out2) {
@@ -134,6 +134,16 @@ public interface PagedOrientationHandler {
int drawableWidth, int drawableHeight, DeviceProfile dp,
@StagePosition int stagePosition);
/**
* Sets positioning and rotation for a SplitInstructionsView.
* @param out The SplitInstructionsView that needs to be positioned.
* @param dp The device profile, used to report rotation and device type.
* @param splitInstructionsHeight The SplitInstructionView's height.
* @param splitInstructionsWidth The SplitInstructionView's width.
*/
void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift);
/**
* @param splitDividerSize height of split screen drag handle in portrait, width in landscape
* @param stagePosition the split position option (top/left, bottom/right) of the first
@@ -27,6 +27,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL;
import static com.android.launcher3.util.DisplayController.NavigationMode.THREE_BUTTONS;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -47,7 +48,9 @@ import android.widget.FrameLayout;
import android.widget.LinearLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition;
@@ -484,6 +487,58 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
}
}
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
out.setPivotY(0);
out.setRotation(getDegreesRotated());
int distanceToEdge;
if ((DisplayController.getNavigationMode(out.getContext()) == THREE_BUTTONS)
&& (dp.isTwoPanels || dp.isTablet)) {
// If 3-button nav is active, align the splitInstructionsView with it.
distanceToEdge = dp.getTaskbarOffsetY()
+ ((dp.taskbarSize - splitInstructionsHeight) / 2);
} else {
// If 3-button nav is not active, set bottom margin according to spec.
if (dp.isPhone) {
if (dp.isLandscape) {
distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
} else {
distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_portrait);
}
} else if (dp.isTwoPanels) {
if (dp.isLandscape) {
distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_twopanels_landscape);
} else {
distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_twopanels_portrait);
}
} else {
if (dp.isLandscape) {
distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_tablet_landscape);
} else {
distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_tablet_portrait);
}
}
}
// Center the view in case of unbalanced insets on left or right of screen
int insetCorrectionX = (dp.getInsets().right - dp.getInsets().left) / 2;
// Adjust for any insets on the bottom edge
int insetCorrectionY = dp.getInsets().bottom;
out.setTranslationX(insetCorrectionX + threeButtonNavShift);
out.setTranslationY(-distanceToEdge + insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
lp.gravity = CENTER_HORIZONTAL | BOTTOM;
out.setLayoutParams(lp);
}
@Override
public void getFinalSplitPlaceholderBounds(int splitDividerSize, DeviceProfile dp,
@StagePosition int stagePosition, Rect out1, Rect out2) {
@@ -19,6 +19,7 @@ package com.android.launcher3.touch;
import static android.view.Gravity.BOTTOM;
import static android.view.Gravity.CENTER_VERTICAL;
import static android.view.Gravity.END;
import static android.view.Gravity.RIGHT;
import static android.view.Gravity.START;
import static com.android.launcher3.touch.SingleAxisSwipeDetector.HORIZONTAL;
@@ -166,6 +167,29 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
STAGE_POSITION_BOTTOM_OR_RIGHT, STAGE_TYPE_MAIN));
}
@Override
public void setSplitInstructionsParams(View out, DeviceProfile dp, int splitInstructionsHeight,
int splitInstructionsWidth, int threeButtonNavShift) {
out.setPivotX(0);
out.setPivotY(0);
out.setRotation(getDegreesRotated());
int distanceToEdge = out.getResources().getDimensionPixelSize(
R.dimen.split_instructions_bottom_margin_phone_landscape);
// Adjust for any insets on the right edge
int insetCorrectionX = dp.getInsets().right;
// Center the view in case of unbalanced insets on top or bottom of screen
int insetCorrectionY = (dp.getInsets().bottom - dp.getInsets().top) / 2;
out.setTranslationX(splitInstructionsWidth - splitInstructionsHeight - distanceToEdge
+ insetCorrectionX);
out.setTranslationY(((splitInstructionsHeight + splitInstructionsWidth) / 2f)
+ insetCorrectionY);
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) out.getLayoutParams();
// Setting gravity to RIGHT instead of the lint-recommended END because we always want this
// view to be screen-right when phone is in seascape, regardless of the RtL setting.
lp.gravity = RIGHT | CENTER_VERTICAL;
out.setLayoutParams(lp);
}
@Override
public void setTaskIconParams(FrameLayout.LayoutParams iconParams,
int taskIconMargin, int taskIconHeight, int thumbnailTopMargin, boolean isRtl) {