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

Commit a74d1e41 authored by Jordan Silva's avatar Jordan Silva Committed by Android (Google) Code Review
Browse files

Merge "Add Accessibility Menu support for multiple DWB banners" into main

parents 4c691339 1b487619
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.view.Surface.ROTATION_0;

import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.window.flags.Flags.enableDesktopWindowingMode;

import android.app.ActivityOptions;
@@ -107,10 +108,14 @@ public interface TaskShortcutFactory {
        public List<SystemShortcut> getShortcuts(RecentsViewContainer container,
                TaskContainer taskContainer) {
            TaskView taskView = taskContainer.getTaskView();
            int actionId = taskContainer.getStagePosition() == STAGE_POSITION_BOTTOM_OR_RIGHT
                    ? R.id.action_app_info_bottom_right
                    : R.id.action_app_info_top_left;

            AppInfo.SplitAccessibilityInfo accessibilityInfo =
                    new AppInfo.SplitAccessibilityInfo(taskView.containsMultipleTasks(),
                            TaskUtils.getTitle(taskView.getContext(), taskContainer.getTask()),
                            taskContainer.getA11yNodeId()
                            actionId
                    );
            return Collections.singletonList(new AppInfo(container, taskContainer.getItemInfo(),
                    taskView, accessibilityInfo));
+38 −1
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import static com.android.launcher3.util.Executors.ORDERED_BG_EXECUTOR;

import android.app.ActivityOptions;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
import android.content.pm.LauncherApps.AppUsageLimit;
@@ -38,6 +39,7 @@ import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.TextView;

@@ -49,6 +51,7 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.systemui.shared.recents.model.Task;

@@ -68,13 +71,15 @@ public final class DigitalWellBeingToast {
    private static final int SPLIT_GRID_BANNER_LARGE = 1;
    /** Used for grid task view, only showing icon */
    private static final int SPLIT_GRID_BANNER_SMALL = 2;

    @IntDef(value = {
            SPLIT_BANNER_FULLSCREEN,
            SPLIT_GRID_BANNER_LARGE,
            SPLIT_GRID_BANNER_SMALL,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface SplitBannerConfig{}
    @interface SplitBannerConfig {
    }

    static final Intent OPEN_APP_USAGE_SETTINGS_TEMPLATE = new Intent(ACTION_APP_USAGE_SETTINGS);
    static final int MINUTE_MS = 60000;
@@ -398,4 +403,36 @@ public final class DigitalWellBeingToast {

        mBanner.setVisibility(visibility);
    }

    private int getAccessibilityActionId() {
        return (mSplitBounds != null
                && mSplitBounds.rightBottomTaskId == mTask.key.id)
                ? R.id.action_digital_wellbeing_bottom_right
                : R.id.action_digital_wellbeing_top_left;
    }

    @Nullable
    public AccessibilityNodeInfo.AccessibilityAction getDWBAccessibilityAction() {
        if (!hasLimit()) {
            return null;
        }

        Context context = mContainer.asContext();
        String label =
                (mTaskView.containsMultipleTasks())
                        ? context.getString(
                        R.string.split_app_usage_settings,
                        TaskUtils.getTitle(context, mTask)
                ) : context.getString(R.string.accessibility_app_usage_settings);
        return new AccessibilityNodeInfo.AccessibilityAction(getAccessibilityActionId(), label);
    }

    public boolean handleAccessibilityAction(int action) {
        if (getAccessibilityActionId() == action) {
            openAppUsageSettings(mTaskView);
            return true;
        } else {
            return false;
        }
    }
}
+51 −22
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.view.View.OnClickListener
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.FrameLayout
import android.widget.Toast
import androidx.annotation.IntDef
@@ -67,7 +68,6 @@ import com.android.launcher3.util.Executors
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SafeCloseable
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
@@ -133,19 +133,26 @@ constructor(
    val taskIds: IntArray
        /** Returns a copy of integer array containing taskIds of all tasks in the TaskView. */
        get() = taskContainers.map { it.task.key.id }.toIntArray()

    val thumbnailViews: Array<TaskThumbnailViewDeprecated>
        get() = taskContainers.map { it.thumbnailViewDeprecated }.toTypedArray()

    val isGridTask: Boolean
        /** Returns whether the task is part of overview grid and not being focused. */
        get() = container.deviceProfile.isTablet && !isFocusedTask

    val isRunningTask: Boolean
        get() = this === recentsView?.runningTaskView

    val isFocusedTask: Boolean
        get() = this === recentsView?.focusedTaskView

    val taskCornerRadius: Float
        get() = currentFullscreenParams.cornerRadius

    val recentsView: RecentsView<*, *>?
        get() = parent as? RecentsView<*, *>

    val pagedOrientationHandler: RecentsPagedOrientationHandler
        get() = orientedState.orientationHandler

@@ -153,10 +160,12 @@ constructor(
    val firstTask: Task
        /** Returns the first task bound to this TaskView. */
        get() = taskContainers[0].task

    @get:Deprecated("Use [taskContainers] instead.")
    val firstThumbnailViewDeprecated: TaskThumbnailViewDeprecated
        /** Returns the first thumbnailView of the TaskView. */
        get() = taskContainers[0].thumbnailViewDeprecated

    @get:Deprecated("Use [taskContainers] instead.")
    val firstItemInfo: ItemInfo
        get() = taskContainers[0].itemInfo
@@ -173,6 +182,7 @@ constructor(
         * not change according to a temporary state.
         */
        get() = Utilities.mapRange(gridProgress, nonGridScale, 1f)

    protected val persistentTranslationX: Float
        /**
         * Returns addition of translationX that is persistent (e.g. fullscreen and grid), and does
@@ -182,42 +192,50 @@ constructor(
            (getNonGridTrans(nonGridTranslationX) +
                getGridTrans(this.gridTranslationX) +
                getNonGridTrans(nonGridPivotTranslationX))

    protected val persistentTranslationY: Float
        /**
         * Returns addition of translationY that is persistent (e.g. fullscreen and grid), and does
         * not change according to a temporary state (e.g. task offset).
         */
        get() = boxTranslationY + getGridTrans(gridTranslationY)

    protected val primarySplitTranslationProperty: FloatProperty<TaskView>
        get() =
            pagedOrientationHandler.getPrimaryValue(
                SPLIT_SELECT_TRANSLATION_X,
                SPLIT_SELECT_TRANSLATION_Y
            )

    protected val secondarySplitTranslationProperty: FloatProperty<TaskView>
        get() =
            pagedOrientationHandler.getSecondaryValue(
                SPLIT_SELECT_TRANSLATION_X,
                SPLIT_SELECT_TRANSLATION_Y
            )

    protected val primaryDismissTranslationProperty: FloatProperty<TaskView>
        get() =
            pagedOrientationHandler.getPrimaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)

    protected val secondaryDismissTranslationProperty: FloatProperty<TaskView>
        get() =
            pagedOrientationHandler.getSecondaryValue(DISMISS_TRANSLATION_X, DISMISS_TRANSLATION_Y)

    protected val primaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
        get() =
            pagedOrientationHandler.getPrimaryValue(
                TASK_OFFSET_TRANSLATION_X,
                TASK_OFFSET_TRANSLATION_Y
            )

    protected val secondaryTaskOffsetTranslationProperty: FloatProperty<TaskView>
        get() =
            pagedOrientationHandler.getSecondaryValue(
                TASK_OFFSET_TRANSLATION_X,
                TASK_OFFSET_TRANSLATION_Y
            )

    protected val taskResistanceTranslationProperty: FloatProperty<TaskView>
        get() =
            pagedOrientationHandler.getSecondaryValue(
@@ -234,6 +252,7 @@ constructor(
    /** Returns a list of all TaskContainers in the TaskView. */
    lateinit var taskContainers: List<TaskContainer>
        protected set

    lateinit var orientedState: RecentsOrientedState

    var taskViewId = UNBOUND_TASK_VIEW_ID
@@ -264,46 +283,55 @@ constructor(
            field = value
            onModalnessUpdated(field)
        }

    protected var taskThumbnailSplashAlpha = 0f
        set(value) {
            field = value
            applyThumbnailSplashAlpha()
        }

    protected var nonGridScale = 1f
        set(value) {
            field = value
            applyScale()
        }

    private var dismissScale = 1f
        set(value) {
            field = value
            applyScale()
        }

    private var dismissTranslationX = 0f
        set(value) {
            field = value
            applyTranslationX()
        }

    private var dismissTranslationY = 0f
        set(value) {
            field = value
            applyTranslationY()
        }

    private var taskOffsetTranslationX = 0f
        set(value) {
            field = value
            applyTranslationX()
        }

    private var taskOffsetTranslationY = 0f
        set(value) {
            field = value
            applyTranslationY()
        }

    private var taskResistanceTranslationX = 0f
        set(value) {
            field = value
            applyTranslationX()
        }

    private var taskResistanceTranslationY = 0f
        set(value) {
            field = value
@@ -321,6 +349,7 @@ constructor(
            field = value
            applyTranslationX()
        }

    var gridTranslationY = 0f
        protected set(value) {
            field = value
@@ -339,6 +368,7 @@ constructor(
            field = value
            applyTranslationX()
        }

    protected var nonGridPivotTranslationX = 0f
        set(value) {
            field = value
@@ -350,16 +380,19 @@ constructor(
            field = value
            applyTranslationY()
        }

    private var splitSelectTranslationX = 0f
        set(value) {
            field = value
            applyTranslationX()
        }

    protected var stableAlpha = 1f
        set(value) {
            field = value
            alpha = stableAlpha
        }

    protected var shouldShowScreenshot = false
        get() = !isRunningTask || field
    /** Enable or disable showing border on hover and focus change */
@@ -375,6 +408,7 @@ constructor(
            hoverBorderAnimator?.setBorderVisibility(visible = field && isHovered, animated = true)
            focusBorderAnimator?.setBorderVisibility(visible = field && isFocused, animated = true)
        }

    protected var iconScaleAnimStartProgress = 0f
    private var focusTransitionProgress = 1f

@@ -522,11 +556,12 @@ constructor(
        super.onInitializeAccessibilityNodeInfo(info)
        with(info) {
            addAction(
                AccessibilityNodeInfo.AccessibilityAction(
                    R.string.accessibility_close,
                AccessibilityAction(
                    R.id.action_close,
                    context.getText(R.string.accessibility_close)
                )
            )

            taskContainers.forEach {
                TraceHelper.allowIpcs("TV.a11yInfo") {
                    TaskOverlayFactory.getEnabledShortcuts(this@TaskView, it).forEach { shortcut ->
@@ -534,15 +569,12 @@ constructor(
                    }
                }
            }
            // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
            if (taskContainers[0].digitalWellBeingToast?.hasLimit() == true) {
                addAction(
                    AccessibilityNodeInfo.AccessibilityAction(
                        R.string.accessibility_app_usage_settings,
                        context.getText(R.string.accessibility_app_usage_settings)
                    )
                )

            // Add DWB accessibility action at the end of the list
            taskContainers.forEach {
                it.digitalWellBeingToast?.getDWBAccessibilityAction()?.let(::addAction)
            }

            recentsView?.let {
                collectionItemInfo =
                    AccessibilityNodeInfo.CollectionItemInfo.obtain(
@@ -557,16 +589,17 @@ constructor(
    }

    override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
        if (action == R.string.accessibility_close) {
        // TODO(b/343708271): Add support for multiple tasks per action.
        if (action == R.id.action_close) {
            recentsView?.dismissTask(this, true /*animateTaskView*/, true /*removeTask*/)
            return true
        }
        if (action == R.string.accessibility_app_usage_settings) {
            // TODO(b/341672022): handle multiple digitalWellBeingToast accessibility actions
            taskContainers[0].digitalWellBeingToast?.openAppUsageSettings(this)

        taskContainers.forEach {
            if (it.digitalWellBeingToast?.handleAccessibilityAction(action) == true) {
                return true
            }
        taskContainers.forEach {

            TaskOverlayFactory.getEnabledShortcuts(this, it).forEach { shortcut ->
                if (shortcut.hasHandlerForAction(action)) {
                    shortcut.onClick(this)
@@ -574,6 +607,7 @@ constructor(
                }
            }
        }

        return super.performAccessibilityAction(action, arguments)
    }

@@ -1555,11 +1589,6 @@ constructor(
    ) {
        val overlay: TaskOverlay<*> = taskOverlayFactory.createOverlay(this)

        @IdRes
        val a11yNodeId: Int =
            if (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) R.id.split_bottomRight_appInfo
            else R.id.split_topLeft_appInfo

        val snapshotView: View
            get() = thumbnailView ?: thumbnailViewDeprecated

+6 −3
Original line number Diff line number Diff line
@@ -19,9 +19,6 @@
    <item type="id" name="view_type_widgets_space" />
    <item type="id" name="view_type_widgets_list" />
    <item type="id" name="view_type_widgets_header" />
    <!--  Used for A11y actions in staged split to identify each task uniquely  -->
    <item type="id" name="split_topLeft_appInfo" />
    <item type="id" name="split_bottomRight_appInfo" />

    <!-- Accessibility actions -->
    <item type="id" name="action_remove" />
@@ -37,6 +34,12 @@
    <item type="id" name="action_remote_action_shortcut" />
    <item type="id" name="action_dismiss_prediction" />
    <item type="id" name="action_pin_prediction"/>
    <item type="id" name="action_close"/>
    <!--  Used for A11y actions in staged split to identify each task uniquely  -->
    <item type="id" name="action_app_info_top_left" />
    <item type="id" name="action_app_info_bottom_right" />
    <item type="id" name="action_digital_wellbeing_top_left" />
    <item type="id" name="action_digital_wellbeing_bottom_right" />

    <!-- QSB IDs. DO not change -->
    <item type="id" name="search_container_workspace" />
+1 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@
    <!-- Title for an option to enter split screen mode for a given app -->
    <string name="recent_task_option_split_screen">Split screen</string>
    <string name="split_app_info_accessibility">App info for %1$s</string>
    <string name="split_app_usage_settings">Usage settings for %1$s</string>

    <!-- App pairs -->
    <string name="save_app_pair">Save app pair</string>