Accessibility actions for lock/phone/camera icon

Bug: 15859203
Change-Id: I02c988f4ed985e4381321aeca89cdfee68f44697
This commit is contained in:
Jorim Jaggi
2014-08-15 18:12:36 +02:00
parent aff1efa622
commit b2e104f8a2
5 changed files with 209 additions and 31 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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();
}

View File

@@ -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);

View File

@@ -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);
}
}