Accessibility actions for lock/phone/camera icon
Bug: 15859203 Change-Id: I02c988f4ed985e4381321aeca89cdfee68f44697
This commit is contained in:
@@ -31,7 +31,7 @@
|
||||
android:layout_gravity="bottom|center_horizontal"
|
||||
android:textStyle="italic"
|
||||
android:textColor="#ffffff"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"/>
|
||||
android:textAppearance="?android:attr/textAppearanceSmall" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/preview_container"
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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.KeyguardIndicationController;
|
||||
import com.android.systemui.statusbar.policy.FlashlightController;
|
||||
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.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);
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) {
|
||||
mCameraImageView.setClickable(touchExplorationEnabled);
|
||||
mPhoneImageView.setClickable(touchExplorationEnabled);
|
||||
mCameraImageView.setFocusable(accessibilityEnabled);
|
||||
mPhoneImageView.setFocusable(accessibilityEnabled);
|
||||
updateLockIconClickability();
|
||||
}
|
||||
|
||||
private void enableAccessibility(boolean touchExplorationEnabled) {
|
||||
mCameraImageView.setOnClickListener(touchExplorationEnabled ? this : null);
|
||||
mCameraImageView.setClickable(touchExplorationEnabled);
|
||||
mPhoneImageView.setOnClickListener(touchExplorationEnabled ? this : null);
|
||||
mPhoneImageView.setClickable(touchExplorationEnabled);
|
||||
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,12 +291,27 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL
|
||||
} else if (v == mPhoneImageView) {
|
||||
launchPhone();
|
||||
} if (v == mLockIcon) {
|
||||
mIndicationController.showTransientIndication(
|
||||
R.string.keyguard_indication_trust_disabled);
|
||||
mLockPatternUtils.requireCredentialEntry(mLockPatternUtils.getCurrentUser());
|
||||
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();
|
||||
Intent intent = getCameraIntent();
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user