diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index 8e9b501970445..ca07c87498d60 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -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" />
+ android:tint="#ffffffff"
+ android:contentDescription="@string/accessibility_unlock_button" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 1d33d7af43bb8..ed3fa58b2fa4b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -213,6 +213,14 @@
Camera
Phone
+
+ Unlock
+
+ unlock
+
+ open phone
+
+ open camera
Switch input method button.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 61246b01c6d25..44d8d239bd682 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -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();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 502c37d546220..652d348afb222 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -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);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
new file mode 100644
index 0000000000000..89ed78723e7ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityController.java
@@ -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 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);
+ }
+}