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

Commit 75d57fa8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "phonewm: refactor Talkback shortcut logic into a controller" into main

parents fbc5e550 d2818e21
Loading
Loading
Loading
Loading
+13 −81
Original line number Diff line number Diff line
@@ -76,7 +76,6 @@ import static android.view.WindowManagerGlobal.ADD_PERMISSION_DENIED;
import static android.view.contentprotection.flags.Flags.createAccessibilityOverlayAppOpEnabled;

import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -97,7 +96,6 @@ import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;
import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -124,7 +122,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -204,8 +201,6 @@ import android.widget.Toast;

import com.android.internal.R;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.display.BrightnessUtils;
@@ -385,8 +380,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {

    public static final String TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD = "waitForAllWindowsDrawn";

    private static final String TALKBACK_LABEL = "TalkBack";

    private static final int POWER_BUTTON_SUPPRESSION_DELAY_DEFAULT_MILLIS = 800;

    /**
@@ -477,6 +470,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
    /** Controller that supports enabling an AccessibilityService by holding down the volume keys */
    private AccessibilityShortcutController mAccessibilityShortcutController;

    private TalkbackShortcutController mTalkbackShortcutController;

    boolean mSafeMode;

    // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key.
@@ -1602,19 +1597,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                if (DEBUG_INPUT) {
                    Slog.d(TAG, "Executing stem primary triple press action behavior.");
                }

                if (Settings.System.getIntForUser(mContext.getContentResolver(),
                        Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
                        /* def= */ 0, UserHandle.USER_CURRENT) == 1) {
                    /** Toggle talkback begin */
                    ComponentName componentName = getTalkbackComponent();
                    if (componentName != null && toggleTalkBack(componentName)) {
                        /** log stem triple press telemetry if it's a talkback enabled event */
                        logStemTriplePressAccessibilityTelemetry(componentName);
                    }
                    performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */ false,
                        /* reason = */ "Stem primary - Triple Press - Toggle Accessibility");
                    /** Toggle talkback end */
                mTalkbackShortcutController.toggleTalkback(mCurrentUserId);
                if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {
                    performHapticFeedback(HapticFeedbackConstants.CONFIRM, /* always = */
                            false, /* reason = */
                            "Stem primary - Triple Press - Toggle Accessibility");
                }
                break;
        }
@@ -1639,61 +1626,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        }
    }

    /**
     * A function that toggles talkback service
     *
     * @return {@code true} if talkback is enabled, {@code false} if talkback is disabled
     */
    private boolean toggleTalkBack(ComponentName componentName) {
        final Set<ComponentName> enabledServices =
                AccessibilityUtils.getEnabledServicesFromSettings(mContext, mCurrentUserId);

        boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName);
        AccessibilityUtils.setAccessibilityServiceState(mContext, componentName,
                !isTalkbackAlreadyEnabled);
        /** if isTalkbackAlreadyEnabled is true, then it's a disabled event so return false
         * and if isTalkbackAlreadyEnabled is false, return true as it's an enabled event */
        return !isTalkbackAlreadyEnabled;
    }

    /**
     * A function that logs stem triple press accessibility telemetry
     * If the user setup (Oobe) is not completed, set the
     * WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE
     * setting which will be later logged via Settings Snapshot
     * else, log ACCESSIBILITY_SHORTCUT_REPORTED atom
     */
    private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) {
        if (!AccessibilityUtils.isUserSetupCompleted(mContext)) {
            Settings.Secure.putInt(mContext.getContentResolver(),
                    Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1);
        } else {
            AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext, componentName,
                    ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE,
                    /* serviceEnabled= */ true);
        }
    }

    private ComponentName getTalkbackComponent() {
        AccessibilityManager accessibilityManager = mContext.getSystemService(
                AccessibilityManager.class);
        List<AccessibilityServiceInfo> serviceInfos =
                accessibilityManager.getInstalledAccessibilityServiceList();

        for (AccessibilityServiceInfo service : serviceInfos) {
            final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
            if (isTalkback(serviceInfo)) {
                return new ComponentName(serviceInfo.packageName, serviceInfo.name);
            }
        }
        return null;
    }

    private boolean isTalkback(ServiceInfo info) {
        String label = info.loadLabel(mPackageManager).toString();
        return label.equals(TALKBACK_LABEL);
    }

    /**
     * Load most recent task (expect current task) and bring it to the front.
     */
@@ -1731,12 +1663,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
            case TRIPLE_PRESS_PRIMARY_NOTHING:
                break;
            case TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY:
                if (Settings.System.getIntForUser(
                                mContext.getContentResolver(),
                                Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
                                /* def= */ 0,
                                UserHandle.USER_CURRENT)
                        == 1) {
                if (mTalkbackShortcutController.isTalkBackShortcutGestureEnabled()) {
                    return 3;
                }
                break;
@@ -2252,6 +2179,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {
            return new ButtonOverridePermissionChecker();
        }

        TalkbackShortcutController getTalkbackShortcutController() {
            return new TalkbackShortcutController(mContext);
        }
    }

    /** {@inheritDoc} */
@@ -2515,6 +2446,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
        mKeyguardDrawnTimeout = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_keyguardDrawnTimeout);
        mKeyguardDelegate = injector.getKeyguardServiceDelegate();
        mTalkbackShortcutController = injector.getTalkbackShortcutController();
        initKeyCombinationRules();
        initSingleKeyGestureRules(injector.getLooper());
        mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
+118 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.server.policy;

import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.UserHandle;
import android.provider.Settings;
import android.view.accessibility.AccessibilityManager;

import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
import com.android.internal.accessibility.util.AccessibilityUtils;
import com.android.internal.annotations.VisibleForTesting;

import java.util.List;
import java.util.Set;

/**
 * This class controls talkback shortcut related operations such as toggling, quering and
 * logging.
 */
@VisibleForTesting
class TalkbackShortcutController {
    private static final String TALKBACK_LABEL = "TalkBack";
    private final Context mContext;
    private final PackageManager mPackageManager;

    TalkbackShortcutController(Context context) {
        mContext = context;
        mPackageManager = mContext.getPackageManager();
    }

    /**
     * A function that toggles talkback service.
     *
     * @return talkback state after toggle. {@code true} if talkback is enabled, {@code false} if
     * talkback is disabled
     */
    boolean toggleTalkback(int userId) {
        final Set<ComponentName> enabledServices =
                AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId);
        ComponentName componentName = getTalkbackComponent();
        boolean isTalkbackAlreadyEnabled = enabledServices.contains(componentName);

        if (isTalkBackShortcutGestureEnabled()) {
            isTalkbackAlreadyEnabled = !isTalkbackAlreadyEnabled;
            AccessibilityUtils.setAccessibilityServiceState(mContext, componentName,
                    isTalkbackAlreadyEnabled);

            // log stem triple press telemetry if it's a talkback enabled event.
            if (componentName != null && isTalkbackAlreadyEnabled) {
                logStemTriplePressAccessibilityTelemetry(componentName);
            }
        }
        return isTalkbackAlreadyEnabled;
    }

    private ComponentName getTalkbackComponent() {
        AccessibilityManager accessibilityManager = mContext.getSystemService(
                AccessibilityManager.class);
        List<AccessibilityServiceInfo> serviceInfos =
                accessibilityManager.getInstalledAccessibilityServiceList();

        for (AccessibilityServiceInfo service : serviceInfos) {
            final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
            if (isTalkback(serviceInfo)) {
                return new ComponentName(serviceInfo.packageName, serviceInfo.name);
            }
        }
        return null;
    }

    boolean isTalkBackShortcutGestureEnabled() {
        return Settings.System.getIntForUser(mContext.getContentResolver(),
                Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
                /* def= */ 0, UserHandle.USER_CURRENT) == 1;
    }

    /**
     * A function that logs stem triple press accessibility telemetry. If the user setup (Oobe)
     * is not completed, set the WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE setting which
     * will be later logged via Settings Snapshot  else, log ACCESSIBILITY_SHORTCUT_REPORTED atom
     */
    private void logStemTriplePressAccessibilityTelemetry(ComponentName componentName) {
        if (!AccessibilityUtils.isUserSetupCompleted(mContext)) {
            Settings.Secure.putInt(mContext.getContentResolver(),
                    Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE, 1);
            return;
        }
        AccessibilityStatsLogUtils.logAccessibilityShortcutActivated(mContext,
                componentName,
                ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE,
                /* serviceEnabled= */ true);
    }

    private boolean isTalkback(ServiceInfo info) {
        return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString());
    }
}
+23 −0
Original line number Diff line number Diff line
@@ -172,6 +172,25 @@ class TestPhoneWindowManager {
    private HandlerThread mHandlerThread;
    private Handler mHandler;

    private boolean mIsTalkBackEnabled;

    class TestTalkbackShortcutController extends TalkbackShortcutController {
        TestTalkbackShortcutController(Context context) {
            super(context);
        }

        @Override
        boolean toggleTalkback(int currentUserId) {
            mIsTalkBackEnabled = !mIsTalkBackEnabled;
            return mIsTalkBackEnabled;
        }

        @Override
        boolean isTalkBackShortcutGestureEnabled() {
            return true;
        }
    }

    private class TestInjector extends PhoneWindowManager.Injector {
        TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
            super(context, funcs, mTestLooper.getLooper());
@@ -197,6 +216,10 @@ class TestPhoneWindowManager {
        PhoneWindowManager.ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {
            return mButtonOverridePermissionChecker;
        }

        TalkbackShortcutController getTalkbackShortcutController() {
            return new TestTalkbackShortcutController(mContext);
        }
    }

    TestPhoneWindowManager(Context context, boolean supportSettingsUpdate) {