Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b0cce863 authored by Jeremy Sim's avatar Jeremy Sim
Browse files

Implement non-disappearing View for split staging instructions

The instructions for how to perform a splitscreen operation, previously conveyed through a disappearing Toast, are now conveyed through a custom View object.

Fixes: 219987907
Test: Manual
Change-Id: Iff2bb6e334e0325e8a091d76a5f9b8767071365f
parent f072b759
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
<?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>
 No newline at end of file
+35 −0
Original line number Diff line number Diff line
<?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>
 No newline at end of file
+15 −1
Original line number Diff line number Diff line
@@ -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();
    }

+123 −0
Original line number Diff line number Diff line
/*
 * 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
Original line number Diff line number Diff line
@@ -390,7 +390,16 @@
    <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>
Loading