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

Commit 8fbcaacd authored by Jorim Jaggi's avatar Jorim Jaggi Committed by Android (Google) Code Review
Browse files

Merge "Accessibility actions for lock/phone/camera icon" into lmp-dev

parents 4a5ccead b2e104f8
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@
        android:layout_gravity="bottom|center_horizontal"
        android:src="@drawable/ic_lock_24dp"
        android:scaleType="center"
        android:tint="#ffffffff" />
        android:tint="#ffffffff"
        android:contentDescription="@string/accessibility_unlock_button" />

</com.android.systemui.statusbar.phone.KeyguardBottomAreaView>
+8 −0
Original line number Diff line number Diff line
@@ -213,6 +213,14 @@
    <string name="accessibility_camera_button">Camera</string>
    <!-- Content description of the phone button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
    <string name="accessibility_phone_button">Phone</string>
    <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
    <string name="accessibility_unlock_button">Unlock</string>
    <!-- Click action label for accessibility for the unlock button. [CHAR LIMIT=NONE] -->
    <string name="unlock_label">unlock</string>
    <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
    <string name="phone_label">open phone</string>
    <!-- Click action label for accessibility for the phone button. [CHAR LIMIT=NONE] -->
    <string name="camera_label">open camera</string>

    <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
    <string name="accessibility_ime_switch_button">Switch input method button.</string>
+102 −28
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.phone.PhoneManager;
@@ -36,7 +37,7 @@ import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.TextView;

@@ -44,17 +45,23 @@ import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.systemui.R;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.FlashlightController;
import com.android.systemui.statusbar.KeyguardAffordanceView;
import com.android.systemui.statusbar.policy.PreviewInflater;

import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;

/**
 * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
 * text.
 */
public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
        UnlockMethodCache.OnUnlockMethodChangedListener {
        UnlockMethodCache.OnUnlockMethodChangedListener,
        AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener {

    final static String TAG = "PhoneStatusBar/KeyguardBottomAreaView";

@@ -80,6 +87,8 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
    private FlashlightController mFlashlightController;
    private PreviewInflater mPreviewInflater;
    private KeyguardIndicationController mIndicationController;
    private AccessibilityController mAccessibilityController;
    private PhoneStatusBar mPhoneStatusBar;

    private final TrustDrawable mTrustDrawable;

@@ -101,6 +110,40 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
        mTrustDrawable = new TrustDrawable(mContext);
    }

    private AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() {
        @Override
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
            super.onInitializeAccessibilityNodeInfo(host, info);
            String label = null;
            if (host == mLockIcon) {
                label = getResources().getString(R.string.unlock_label);
            } else if (host == mCameraImageView) {
                label = getResources().getString(R.string.camera_label);
            } else if (host == mPhoneImageView) {
                label = getResources().getString(R.string.phone_label);
            }
            info.addAction(new AccessibilityAction(ACTION_CLICK, label));
        }

        @Override
        public boolean performAccessibilityAction(View host, int action, Bundle args) {
            if (action == ACTION_CLICK) {
                if (host == mLockIcon) {
                    mPhoneStatusBar.animateCollapsePanels(
                            CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
                    return true;
                } else if (host == mCameraImageView) {
                    launchCamera();
                    return true;
                } else if (host == mPhoneImageView) {
                    launchPhone();
                    return true;
                }
            }
            return super.performAccessibilityAction(host, action, args);
        }
    };

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
@@ -111,7 +154,6 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
        mLockIcon = (KeyguardAffordanceView) findViewById(R.id.lock_icon);
        mIndicationText = (TextView) findViewById(R.id.keyguard_indication_text);
        watchForCameraPolicyChanges();
        watchForAccessibilityChanges();
        updateCameraVisibility();
        updatePhoneVisibility();
        mUnlockMethodCache = UnlockMethodCache.getInstance(getContext());
@@ -123,6 +165,16 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
        inflatePreviews();
        mLockIcon.setOnClickListener(this);
        mLockIcon.setBackground(mTrustDrawable);
        mLockIcon.setOnLongClickListener(this);
        mCameraImageView.setOnClickListener(this);
        mPhoneImageView.setOnClickListener(this);
        initAccessibility();
    }

    private void initAccessibility() {
        mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
        mPhoneImageView.setAccessibilityDelegate(mAccessibilityDelegate);
        mCameraImageView.setAccessibilityDelegate(mAccessibilityDelegate);
    }

    @Override
@@ -150,6 +202,15 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
        mFlashlightController = flashlightController;
    }

    public void setAccessibilityController(AccessibilityController accessibilityController) {
        mAccessibilityController = accessibilityController;
        accessibilityController.addStateChangedCallback(this);
    }

    public void setPhoneStatusBar(PhoneStatusBar phoneStatusBar) {
        mPhoneStatusBar = phoneStatusBar;
    }

    private Intent getCameraIntent() {
        KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
        boolean currentUserHasTrust = updateMonitor.getUserHasTrust(
@@ -203,28 +264,24 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
    }

    private void watchForAccessibilityChanges() {
        final AccessibilityManager am =
                (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);

        // Set the initial state
        enableAccessibility(am.isTouchExplorationEnabled());

        // Watch for changes
        am.addTouchExplorationStateChangeListener(
                new AccessibilityManager.TouchExplorationStateChangeListener() {
    @Override
            public void onTouchExplorationStateChanged(boolean enabled) {
                enableAccessibility(enabled);
            }
        });
    }

    private void enableAccessibility(boolean touchExplorationEnabled) {
        mCameraImageView.setOnClickListener(touchExplorationEnabled ? this : null);
    public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) {
        mCameraImageView.setClickable(touchExplorationEnabled);
        mPhoneImageView.setOnClickListener(touchExplorationEnabled ? this : null);
        mPhoneImageView.setClickable(touchExplorationEnabled);
        mCameraImageView.setFocusable(accessibilityEnabled);
        mPhoneImageView.setFocusable(accessibilityEnabled);
        updateLockIconClickability();
    }

    private void updateLockIconClickability() {
        if (mAccessibilityController == null) {
            return;
        }
        mLockIcon.setClickable(mUnlockMethodCache.isTrustManaged()
                || mAccessibilityController.isTouchExplorationEnabled());
        mLockIcon.setLongClickable(mAccessibilityController.isTouchExplorationEnabled()
                && mUnlockMethodCache.isTrustManaged());
        mLockIcon.setFocusable(mAccessibilityController.isAccessibilityEnabled());
    }

    @Override
@@ -234,11 +291,26 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
        } else if (v == mPhoneImageView) {
            launchPhone();
        } if (v == mLockIcon) {
            if (!mAccessibilityController.isAccessibilityEnabled()) {
                handleTrustCircleClick();
            } else {
                mPhoneStatusBar.animateCollapsePanels(
                        CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
            }
        }
    }

    @Override
    public boolean onLongClick(View v) {
        handleTrustCircleClick();
        return true;
    }

    private void handleTrustCircleClick() {
        mIndicationController.showTransientIndication(
                R.string.keyguard_indication_trust_disabled);
        mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser());
    }
    }

    public void launchCamera() {
        mFlashlightController.killFlashlight();
@@ -304,7 +376,9 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
        mLockIcon.setImageResource(iconRes);
        boolean trustManaged = mUnlockMethodCache.isTrustManaged();
        mTrustDrawable.setTrustManaged(trustManaged);
        mLockIcon.setClickable(trustManaged);

        // TODO: Update content description depending on state
        updateLockIconClickability();
    }


+5 −0
Original line number Diff line number Diff line
@@ -132,6 +132,7 @@ import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.SpeedBumpView;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
@@ -237,6 +238,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
    NextAlarmController mNextAlarmController;
    KeyguardMonitor mKeyguardMonitor;
    BrightnessMirrorController mBrightnessMirrorController;
    AccessibilityController mAccessibilityController;

    int mNaturalBarHeight = -1;
    int mIconSize = -1;
@@ -805,6 +807,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,

        mFlashlightController = new FlashlightController(mContext);
        mKeyguardBottomArea.setFlashlightController(mFlashlightController);
        mKeyguardBottomArea.setPhoneStatusBar(this);
        mAccessibilityController = new AccessibilityController(mContext);
        mKeyguardBottomArea.setAccessibilityController(mAccessibilityController);
        mNextAlarmController = new NextAlarmController(mContext);
        mKeyguardMonitor = new KeyguardMonitor();
        mUserSwitcherController = new UserSwitcherController(mContext, mKeyguardMonitor);
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 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.systemui.statusbar.policy;

import android.content.Context;
import android.util.Log;
import android.view.accessibility.AccessibilityManager;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;

public class AccessibilityController implements
        AccessibilityManager.AccessibilityStateChangeListener,
        AccessibilityManager.TouchExplorationStateChangeListener {

    private final ArrayList<AccessibilityStateChangedCallback> mChangeCallbacks = new ArrayList<>();

    private boolean mAccessibilityEnabled;
    private boolean mTouchExplorationEnabled;

    public AccessibilityController(Context context) {
        AccessibilityManager am =
                (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
        am.addTouchExplorationStateChangeListener(this);
        am.addAccessibilityStateChangeListener(this);
        mAccessibilityEnabled = am.isEnabled();
        mTouchExplorationEnabled = am.isTouchExplorationEnabled();
    }

    public boolean isAccessibilityEnabled() {
        return mAccessibilityEnabled;
    }

    public boolean isTouchExplorationEnabled() {
        return mTouchExplorationEnabled;
    }

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("AccessibilityController state:");
        pw.print("  mAccessibilityEnabled="); pw.println(mAccessibilityEnabled);
        pw.print("  mTouchExplorationEnabled="); pw.println(mTouchExplorationEnabled);
    }

    public void addStateChangedCallback(AccessibilityStateChangedCallback cb) {
        mChangeCallbacks.add(cb);
        cb.onStateChanged(mAccessibilityEnabled, mTouchExplorationEnabled);
    }

    public void removeStateChangedCallback(AccessibilityStateChangedCallback cb) {
        mChangeCallbacks.remove(cb);
    }

    private void fireChanged() {
        final int N = mChangeCallbacks.size();
        for (int i = 0; i < N; i++) {
            mChangeCallbacks.get(i).onStateChanged(mAccessibilityEnabled, mTouchExplorationEnabled);
        }
    }

    @Override
    public void onAccessibilityStateChanged(boolean enabled) {
        mAccessibilityEnabled = enabled;
        fireChanged();
    }

    @Override
    public void onTouchExplorationStateChanged(boolean enabled) {
        mTouchExplorationEnabled = enabled;
        fireChanged();
    }

    public interface AccessibilityStateChangedCallback {
        void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled);
    }
}