summaryrefslogtreecommitdiffstats
path: root/packages/SystemUI
diff options
context:
space:
mode:
Diffstat (limited to 'packages/SystemUI')
-rw-r--r--packages/SystemUI/Android.mk6
-rw-r--r--packages/SystemUI/AndroidManifest.xml19
-rw-r--r--packages/SystemUI/res/drawable/ic_notify_button_bg.xml4
-rw-r--r--packages/SystemUI/res/layout-sw600dp/super_status_bar.xml52
-rw-r--r--packages/SystemUI/res/layout/navigation_bar.xml1
-rw-r--r--packages/SystemUI/res/layout/status_bar.xml1
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded.xml12
-rw-r--r--packages/SystemUI/res/layout/status_bar_expanded_header.xml25
-rw-r--r--packages/SystemUI/res/layout/status_bar_flip_button.xml37
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml51
-rw-r--r--packages/SystemUI/res/layout/status_bar_notification_row.xml1
-rw-r--r--packages/SystemUI/res/layout/super_status_bar.xml25
-rw-r--r--packages/SystemUI/res/layout/user_switcher_host.xml36
-rw-r--r--packages/SystemUI/res/layout/user_switcher_item.xml40
-rw-r--r--packages/SystemUI/res/values-sw600dp/config.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml9
-rw-r--r--packages/SystemUI/res/values/colors.xml6
-rw-r--r--packages/SystemUI/res/values/config.xml4
-rw-r--r--packages/SystemUI/res/values/dimens.xml6
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/res/values/styles.xml4
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java111
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIService.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java207
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java1369
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java172
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java118
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java139
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java68
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java226
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java194
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java200
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java14
47 files changed, 3237 insertions, 283 deletions
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index f8f064a..69c6159 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -6,6 +6,7 @@ LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/com/android/systemui/EventLogTags.logtags
+LOCAL_STATIC_JAVA_LIBRARIES := Keyguard
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_PACKAGE_NAME := SystemUI
@@ -14,6 +15,11 @@ LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+LOCAL_RESOURCE_DIR := \
+ frameworks/base/packages/Keyguard/res \
+ $(LOCAL_PATH)/res
+LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.keyguard
+
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 3424eed..d371d70 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -89,11 +89,20 @@
<!-- Keyguard -->
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.GET_ACCOUNTS" />
+ <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
+ <uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" />
+ <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
+ <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
+ <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <uses-permission android:name="android.permission.TRUST_LISTENER" />
<!-- Wifi Display -->
<uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
<application
+ android:name=".SystemUIApplication"
android:persistent="true"
android:allowClearUserData="false"
android:allowBackup="false"
@@ -101,7 +110,11 @@
android:label="@string/app_label"
android:icon="@*android:drawable/platlogo"
android:process="com.android.systemui"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:theme="@style/systemui_theme">
+ <!-- Keep theme in sync with SystemUIApplication.onCreate().
+ Setting the theme on the application does not affect views inflated by services.
+ The application theme is set again from onCreate to take effect for those views. -->
<!-- Broadcast receiver that gets the broadcast at boot time and starts
up everything else.
@@ -258,6 +271,10 @@
</intent-filter>
</service>
+ <service
+ android:name=".keyguard.KeyguardService"
+ android:exported="true" />
+
<activity android:name=".Somnambulator"
android:label="@string/start_dreams"
android:icon="@mipmap/ic_launcher_dreams"
diff --git a/packages/SystemUI/res/drawable/ic_notify_button_bg.xml b/packages/SystemUI/res/drawable/ic_notify_button_bg.xml
index 85f1ea2..3a47261 100644
--- a/packages/SystemUI/res/drawable/ic_notify_button_bg.xml
+++ b/packages/SystemUI/res/drawable/ic_notify_button_bg.xml
@@ -15,6 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_pressed="true" android:drawable="@*android:drawable/list_selector_pressed_holo_dark" />
- <item android:drawable="@*android:drawable/list_selector_disabled_holo_dark" />
+ <item android:state_pressed="true"
+ android:drawable="@*android:drawable/list_selector_pressed_holo_dark" />
</selector>
diff --git a/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml b/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml
deleted file mode 100644
index 0947c6f..0000000
--- a/packages/SystemUI/res/layout-sw600dp/super_status_bar.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2012, 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.
-*/
--->
-
-<!-- This is the combined status bar / notification panel window. -->
-<com.android.systemui.statusbar.phone.StatusBarWindowView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
- android:focusable="true"
- android:descendantFocusability="afterDescendants"
- android:fitsSystemWindows="true"
- >
-
- <include layout="@layout/status_bar"
- android:layout_width="match_parent"
- android:layout_height="@*android:dimen/status_bar_height"
- />
-
-
- <com.android.systemui.statusbar.phone.PanelHolder
- android:id="@+id/panel_holder"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginTop="@*android:dimen/status_bar_height"
- >
- <include layout="@layout/status_bar_expanded"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="wrap_content"
- android:layout_gravity="start|top"
- />
- <include layout="@layout/quick_settings"
- android:layout_width="@dimen/notification_panel_width"
- android:layout_height="wrap_content"
- android:layout_gravity="end|top"
- />
- </com.android.systemui.statusbar.phone.PanelHolder>
-</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index 5488a87..a6fb443 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -169,6 +169,7 @@
android:scaleType="center"
android:visibility="gone"
android:contentDescription="@string/accessibility_camera_button"
+ systemui:glowBackground="@drawable/ic_sysbar_highlight_land"
/>
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index ea6be1b..1b35537 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -27,7 +27,6 @@
android:orientation="vertical"
android:focusable="true"
android:descendantFocusability="afterDescendants"
- android:fitsSystemWindows="true"
>
<ImageView
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index a7ec064..8f4417e 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -36,6 +36,14 @@
android:layout_gravity="bottom"
/>
+ <ViewStub android:id="@+id/keyguard_flip_stub"
+ android:layout="@layout/status_bar_flip_button"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:layout_gravity="right|top"
+ android:layout_marginTop="@*android:dimen/status_bar_height"
+ android:visibility="gone" />
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -49,6 +57,10 @@
android:layout_height="@dimen/notification_panel_header_height"
/>
+ <include
+ layout="@layout/keyguard_status_view"
+ android:visibility="gone" />
+
<TextView
android:id="@+id/emergency_calls_only"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network.EmergencyOnly"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 9aa7cfd..56523db 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -81,29 +81,10 @@
android:src="@drawable/ic_notify_clear"
android:background="@drawable/ic_notify_button_bg"
android:contentDescription="@string/accessibility_clear_all"
- />
+ />
- <FrameLayout android:id="@+id/settings_button_holder"
+ <include layout="@layout/status_bar_flip_button"
android:layout_width="50dp"
android:layout_height="50dp"
- android:layout_marginStart="12dp"
- >
- <ImageView android:id="@+id/settings_button"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:scaleType="center"
- android:src="@drawable/ic_notify_settings"
- android:background="@drawable/ic_notify_button_bg"
- android:contentDescription="@string/accessibility_desc_quick_settings"
- />
- <ImageView android:id="@+id/notification_button"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:scaleType="center"
- android:src="@drawable/ic_notifications"
- android:background="@drawable/ic_notify_button_bg"
- android:visibility="gone"
- android:contentDescription="@string/accessibility_notifications_button"
- />
- </FrameLayout>
+ android:layout_marginStart="12dp" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_flip_button.xml b/packages/SystemUI/res/layout/status_bar_flip_button.xml
new file mode 100644
index 0000000..db672ea
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_flip_button.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/settings_button_holder"
+ android:layout_width="50dp"
+ android:layout_height="50dp">
+ <ImageView android:id="@+id/settings_button"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:scaleType="center"
+ android:src="@drawable/ic_notify_settings"
+ android:background="@drawable/ic_notify_button_bg"
+ android:contentDescription="@string/accessibility_desc_quick_settings" />
+ <ImageView android:id="@+id/notification_button"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:scaleType="center"
+ android:src="@drawable/ic_notifications"
+ android:background="@drawable/ic_notify_button_bg"
+ android:visibility="gone"
+ android:contentDescription="@string/accessibility_notifications_button" />
+</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
new file mode 100644
index 0000000..79b03ce
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -0,0 +1,51 @@
+<!--
+ ~ 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
+ -->
+
+<FrameLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <com.android.systemui.statusbar.LatestItemView
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="40dp"
+ android:layout_marginTop="@dimen/notification_divider_height"
+ android:focusable="true"
+ android:clickable="true"
+ android:background="@*android:drawable/notification_quantum_bg_dim"
+ >
+ <TextView
+ android:id="@+id/more_text"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:gravity="center_horizontal"
+ android:textColor="@color/keyguard_overflow_content_color"
+ android:textAllCaps="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ />
+ <com.android.systemui.statusbar.NotificationOverflowIconsView
+ android:id="@+id/overflow_icons_view"
+ android:layout_gravity="end|center_vertical"
+ android:gravity="end"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:layout_width="120dp"
+ android:layout_height="wrap_content"
+ />
+ </com.android.systemui.statusbar.LatestItemView>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index e74e568..d61d8b9 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -32,6 +32,7 @@
android:layout_marginTop="@dimen/notification_divider_height"
android:focusable="true"
android:clickable="true"
+ android:background="@*android:drawable/notification_quantum_bg"
>
<com.android.internal.widget.SizeAdaptiveLayout android:id="@+id/expanded"
diff --git a/packages/SystemUI/res/layout/super_status_bar.xml b/packages/SystemUI/res/layout/super_status_bar.xml
index 2b56618..9176d24 100644
--- a/packages/SystemUI/res/layout/super_status_bar.xml
+++ b/packages/SystemUI/res/layout/super_status_bar.xml
@@ -20,32 +20,29 @@
<!-- This is the combined status bar / notification panel window. -->
<com.android.systemui.statusbar.phone.StatusBarWindowView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
android:focusable="true"
- android:descendantFocusability="afterDescendants"
- android:fitsSystemWindows="true"
- android:background="@android:color/transparent"
- >
+ android:descendantFocusability="afterDescendants">
<include layout="@layout/status_bar"
android:layout_width="match_parent"
- android:layout_height="@*android:dimen/status_bar_height"
- />
+ android:layout_height="@*android:dimen/status_bar_height" />
<com.android.systemui.statusbar.phone.PanelHolder
android:id="@+id/panel_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
- >
+ android:layout_marginTop="@dimen/panel_holder_padding_top"
+ android:layout_marginBottom="@*android:dimen/navigation_bar_height">
<include layout="@layout/status_bar_expanded"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
+ android:layout_width="@dimen/notification_panel_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start|top" />
<ViewStub android:id="@+id/quick_settings_stub"
android:layout="@layout/quick_settings"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- />
+ android:layout_width="@dimen/notification_panel_width"
+ android:layout_height="match_parent" />
</com.android.systemui.statusbar.phone.PanelHolder>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
diff --git a/packages/SystemUI/res/layout/user_switcher_host.xml b/packages/SystemUI/res/layout/user_switcher_host.xml
new file mode 100644
index 0000000..bc56cf6
--- /dev/null
+++ b/packages/SystemUI/res/layout/user_switcher_host.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ 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
+ -->
+
+<!-- FrameLayout -->
+<com.android.systemui.settings.UserSwitcherHostView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="#dd000000">
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@*android:dimen/volume_panel_top"
+ android:background="@*android:drawable/dialog_full_holo_dark">
+ <ListView android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:listitem="@layout/user_switcher_item"/>
+ </FrameLayout>
+</com.android.systemui.settings.UserSwitcherHostView> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/user_switcher_item.xml b/packages/SystemUI/res/layout/user_switcher_item.xml
new file mode 100644
index 0000000..43a85e7
--- /dev/null
+++ b/packages/SystemUI/res/layout/user_switcher_item.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:orientation="horizontal"
+ tools:context=".settings.UserSwitcherDialog">
+ <ImageView
+ android:layout_width="64dp"
+ android:layout_height="match_parent"
+ android:id="@+id/user_picture"
+ tools:src="@drawable/dessert_zombiegingerbread"/>
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:id="@+id/user_name"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:padding="8dp"
+ android:gravity="center_vertical"
+ tools:text="Hiroshi Lockheimer"
+ />
+</LinearLayout>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index b77f1e0..440ead6 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -30,5 +30,5 @@
<integer name="quick_settings_user_time_settings_tile_span">1</integer>
<!-- Enable the "flip settings" panel -->
- <bool name="config_hasFlipSettingsPanel">false</bool>
+ <bool name="config_hasFlipSettingsPanel">true</bool>
</resources>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 45f6af3..b4fafec 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -24,8 +24,8 @@
<dimen name="notification_panel_margin_left">16dp</dimen>
<!-- Gravity for the notification & quick settings panels -->
- <!-- 0x800033 = start|top ; 0x800035 = end|top -->
- <integer name="notification_panel_layout_gravity">0x800033</integer>
+ <!-- 0x31 = top|center_horizontal ; 0x800035 = end|top -->
+ <integer name="notification_panel_layout_gravity">0x31</integer>
<integer name="settings_panel_layout_gravity">0x800035</integer>
<!-- Diameter of outer shape drawable shown in navbar search-->
@@ -41,9 +41,8 @@
<dimen name="status_bar_recents_thumbnail_width">200dp</dimen>
<dimen name="status_bar_recents_thumbnail_height">177dp</dimen>
- <!-- On tablet-sized devices, we allocate the rightmost third(ish) of the draggable status bar
- to quick settings. -->
- <item type="dimen" name="settings_panel_dragzone_fraction">35%</item>
+ <!-- On tablets, panels drop from the statusbar instead of overlapping it. -->
+ <dimen name="panel_holder_padding_top">@*android:dimen/status_bar_height</dimen>
<!-- Minimum fraction of the screen that should be taken up by the notification panel. -->
<item type="dimen" name="notification_panel_min_height_frac">40%</item>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 5cf0453..59e8360 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -31,7 +31,8 @@
<drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable>
<drawable name="heads_up_notification_bg_pressed">#ff33B5E5</drawable>
<drawable name="notification_header_bg">#FF000000</drawable>
- <color name="notification_panel_scrim_color">#B0000000</color>
+ <color name="notification_panel_scrim_color">#A0000000</color>
+ <color name="notification_panel_scrim_color_keyguard">#80000000</color>
<color name="batterymeter_frame_color">#66FFFFFF</color><!-- 40% white -->
<color name="batterymeter_charge_color">#FFFFFFFF</color>
<color name="batterymeter_bolt_color">#FFFFFFFF</color>
@@ -45,4 +46,7 @@
<!-- Tint color for active Quick Settings icons. -->
<color name="ic_qs_on">#ffffffff</color>
+
+ <!-- Tint color for the content on the notification overflow card. -->
+ <color name="keyguard_overflow_content_color">#ff666666</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 672c1d0..e305d94 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -114,5 +114,9 @@
<integer name="recents_filter_animate_new_views_min_duration">125</integer>
<!-- The min animation duration for animating views that are newly visible. -->
<integer name="recents_animate_task_bar_enter_duration">200</integer>
+
+ <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
+ card. -->
+ <integer name="keyguard_max_notification_count">4</integer>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2c8f9a1..5e7db8b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -167,6 +167,9 @@
<!-- Extra space above the clock in the panel -->
<dimen name="notification_panel_header_padding_top">0dp</dimen>
+ <!-- Extra space above the panel holder -->
+ <dimen name="panel_holder_padding_top">0dp</dimen>
+
<!-- Layout parameters for the notification panel -->
<dimen name="notification_panel_margin_bottom">0dp</dimen>
<dimen name="notification_panel_margin_left">0dp</dimen>
@@ -257,4 +260,7 @@
<!-- Width of the zen mode interstitial dialog. -->
<dimen name="zen_mode_dialog_width">320dp</dimen>
+
+ <!-- Camera affordance drag distance -->
+ <dimen name="camera_drag_distance">100dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index ad10545..d994a5b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -534,4 +534,9 @@
</plurals>
<!-- Zen mode: Summary notification content text. [CHAR LIMIT=NONE] -->
<string name="zen_mode_notification_text">Touch to show</string>
+
+ <!-- Text for overflow card on Keyguard when there is not enough space for all notifications on Keyguard. [CHAR LIMIT=12] -->
+ <plurals name="keyguard_more_overflow_text">
+ <item quantity="other">%d more</item>
+ </plurals>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 76cadd7..c2d584b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -169,5 +169,7 @@
<!-- Note: must be dp to fit in status bar -->
<item name="android:textSize">14dp</item>
</style>
-
+
+ <style name="systemui_theme" parent="@android:style/Theme.DeviceDefault" />
+
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
new file mode 100644
index 0000000..0f55683
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -0,0 +1,111 @@
+/*
+ * 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;
+
+import android.app.Application;
+import android.content.res.Configuration;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Application class for SystemUI.
+ */
+public class SystemUIApplication extends Application {
+
+ private static final String TAG = "SystemUIService";
+ private static final boolean DEBUG = false;
+
+ /**
+ * The classes of the stuff to start.
+ */
+ private final Class<?>[] SERVICES = new Class[] {
+ com.android.systemui.keyguard.KeyguardViewMediator.class,
+ com.android.systemui.recent.Recents.class,
+ com.android.systemui.statusbar.SystemBars.class,
+ com.android.systemui.usb.StorageNotification.class,
+ com.android.systemui.power.PowerUI.class,
+ com.android.systemui.media.RingtonePlayer.class,
+ com.android.systemui.settings.SettingsUI.class,
+ };
+
+ /**
+ * Hold a reference on the stuff we start.
+ */
+ private final SystemUI[] mServices = new SystemUI[SERVICES.length];
+ private boolean mServicesStarted;
+ private final Map<Class<?>, Object> mComponents = new HashMap<Class<?>, Object>();
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // Set the application theme that is inherited by all services. Note that setting the
+ // application theme in the manifest does only work for activities. Keep this in sync with
+ // the theme set there.
+ setTheme(R.style.systemui_theme);
+ }
+
+ /**
+ * Makes sure that all the SystemUI services are running. If they are already running, this is a
+ * no-op. This is needed to conditinally start all the services, as we only need to have it in
+ * the main process.
+ *
+ * <p>This method must only be called from the main thread.</p>
+ */
+ public void startServicesIfNeeded() {
+ if (mServicesStarted) {
+ return;
+ }
+ final int N = SERVICES.length;
+ for (int i=0; i<N; i++) {
+ Class<?> cl = SERVICES[i];
+ if (DEBUG) Log.d(TAG, "loading: " + cl);
+ try {
+ mServices[i] = (SystemUI)cl.newInstance();
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ } catch (InstantiationException ex) {
+ throw new RuntimeException(ex);
+ }
+ mServices[i].mContext = this;
+ mServices[i].mComponents = mComponents;
+ if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
+ mServices[i].start();
+ }
+ mServicesStarted = true;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ if (mServicesStarted) {
+ int len = mServices.length;
+ for (int i = 0; i < len; i++) {
+ mServices[i].onConfigurationChanged(newConfig);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> T getComponent(Class<T> interfaceType) {
+ return (T) mComponents.get(interfaceType);
+ }
+
+ public SystemUI[] getServices() {
+ return mServices;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index ca5f7d1..05e5f6b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -18,65 +18,19 @@ package com.android.systemui;
import android.app.Service;
import android.content.Intent;
-import android.content.res.Configuration;
import android.os.IBinder;
-import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.HashMap;
public class SystemUIService extends Service {
- private static final String TAG = "SystemUIService";
-
- /**
- * The classes of the stuff to start.
- */
- private final Class<?>[] SERVICES = new Class[] {
- com.android.systemui.recent.Recents.class,
- com.android.systemui.statusbar.SystemBars.class,
- com.android.systemui.usb.StorageNotification.class,
- com.android.systemui.power.PowerUI.class,
- com.android.systemui.media.RingtonePlayer.class,
- com.android.systemui.settings.SettingsUI.class,
- };
-
- /**
- * Hold a reference on the stuff we start.
- */
- private final SystemUI[] mServices = new SystemUI[SERVICES.length];
@Override
public void onCreate() {
- HashMap<Class<?>, Object> components = new HashMap<Class<?>, Object>();
- final int N = SERVICES.length;
- for (int i=0; i<N; i++) {
- Class<?> cl = SERVICES[i];
- Log.d(TAG, "loading: " + cl);
- try {
- mServices[i] = (SystemUI)cl.newInstance();
- } catch (IllegalAccessException ex) {
- throw new RuntimeException(ex);
- } catch (InstantiationException ex) {
- throw new RuntimeException(ex);
- }
- mServices[i].mContext = this;
- mServices[i].mComponents = components;
- Log.d(TAG, "running: " + mServices[i]);
- mServices[i].start();
- }
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- for (SystemUI ui: mServices) {
- ui.onConfigurationChanged(newConfig);
- }
+ super.onCreate();
+ ((SystemUIApplication) getApplication()).startServicesIfNeeded();
}
- /**
- * Nobody binds to us.
- */
@Override
public IBinder onBind(Intent intent) {
return null;
@@ -84,14 +38,15 @@ public class SystemUIService extends Service {
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ SystemUI[] services = ((SystemUIApplication) getApplication()).getServices();
if (args == null || args.length == 0) {
- for (SystemUI ui: mServices) {
+ for (SystemUI ui: services) {
pw.println("dumping service: " + ui.getClass().getName());
ui.dump(fd, pw, args);
}
} else {
String svc = args[0];
- for (SystemUI ui: mServices) {
+ for (SystemUI ui: services) {
String name = ui.getClass().getName();
if (name.endsWith(svc)) {
ui.dump(fd, pw, args);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
new file mode 100644
index 0000000..41c0e78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -0,0 +1,207 @@
+/*
+ * 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.keyguard;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.MotionEvent;
+
+import com.android.internal.policy.IKeyguardExitCallback;
+import com.android.internal.policy.IKeyguardService;
+import com.android.internal.policy.IKeyguardServiceConstants;
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.systemui.SystemUIApplication;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+public class KeyguardService extends Service {
+ static final String TAG = "KeyguardService";
+ static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
+
+ private KeyguardViewMediator mKeyguardViewMediator;
+
+ @Override
+ public void onCreate() {
+ ((SystemUIApplication) getApplication()).startServicesIfNeeded();
+ mKeyguardViewMediator =
+ ((SystemUIApplication) getApplication()).getComponent(KeyguardViewMediator.class);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ void checkPermission() {
+ if (getBaseContext().checkCallingOrSelfPermission(PERMISSION) != PERMISSION_GRANTED) {
+ Log.w(TAG, "Caller needs permission '" + PERMISSION + "' to call " + Debug.getCaller());
+ throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
+ + ", must have permission " + PERMISSION);
+ }
+ }
+
+ private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
+
+ private boolean mIsOccluded;
+
+ @Override
+ public boolean isShowing() {
+ return mKeyguardViewMediator.isShowing();
+ }
+
+ @Override
+ public boolean isSecure() {
+ return mKeyguardViewMediator.isSecure();
+ }
+
+ @Override
+ public boolean isShowingAndNotOccluded() {
+ return mKeyguardViewMediator.isShowingAndNotOccluded();
+ }
+
+ @Override
+ public boolean isInputRestricted() {
+ return mKeyguardViewMediator.isInputRestricted();
+ }
+
+ @Override
+ public void verifyUnlock(IKeyguardExitCallback callback) {
+ checkPermission();
+ mKeyguardViewMediator.verifyUnlock(callback);
+ }
+
+ @Override
+ public void keyguardDone(boolean authenticated, boolean wakeup) {
+ checkPermission();
+ mKeyguardViewMediator.keyguardDone(authenticated, wakeup);
+ }
+
+ @Override
+ public int setOccluded(boolean isOccluded) {
+ checkPermission();
+ synchronized (this) {
+ int result;
+ if (isOccluded && mKeyguardViewMediator.isShowing()
+ && !mIsOccluded) {
+ result = IKeyguardServiceConstants
+ .KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_UNSET_FLAGS;
+ } else if (!isOccluded && mKeyguardViewMediator.isShowing()
+ && mIsOccluded) {
+ result = IKeyguardServiceConstants
+ .KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_SET_FLAGS;
+ } else {
+ result = IKeyguardServiceConstants.KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_NONE;
+ }
+ if (mIsOccluded != isOccluded) {
+ mKeyguardViewMediator.setOccluded(isOccluded);
+
+ // Cache the value so we always have a fresh view in whether Keyguard is occluded.
+ // If we would just call mKeyguardViewMediator.isOccluded(), this might be stale
+ // because that value gets updated in another thread.
+ mIsOccluded = isOccluded;
+ }
+ return result;
+ }
+ }
+
+ @Override
+ public void dismiss() {
+ checkPermission();
+ mKeyguardViewMediator.dismiss();
+ }
+
+ @Override
+ public void onDreamingStarted() {
+ checkPermission();
+ mKeyguardViewMediator.onDreamingStarted();
+ }
+
+ @Override
+ public void onDreamingStopped() {
+ checkPermission();
+ mKeyguardViewMediator.onDreamingStopped();
+ }
+
+ @Override
+ public void onScreenTurnedOff(int reason) {
+ checkPermission();
+ mKeyguardViewMediator.onScreenTurnedOff(reason);
+ }
+
+ @Override
+ public void onScreenTurnedOn(IKeyguardShowCallback callback) {
+ checkPermission();
+ mKeyguardViewMediator.onScreenTurnedOn(callback);
+ }
+
+ @Override
+ public void setKeyguardEnabled(boolean enabled) {
+ checkPermission();
+ mKeyguardViewMediator.setKeyguardEnabled(enabled);
+ }
+
+ @Override
+ public boolean isDismissable() {
+ return mKeyguardViewMediator.isDismissable();
+ }
+
+ @Override
+ public void onSystemReady() {
+ checkPermission();
+ mKeyguardViewMediator.onSystemReady();
+ }
+
+ @Override
+ public void doKeyguardTimeout(Bundle options) {
+ checkPermission();
+ mKeyguardViewMediator.doKeyguardTimeout(options);
+ }
+
+ @Override
+ public void setCurrentUser(int userId) {
+ checkPermission();
+ mKeyguardViewMediator.setCurrentUser(userId);
+ }
+
+ @Override
+ public void showAssistant() {
+ checkPermission();
+ }
+
+ @Override
+ public void dispatch(MotionEvent event) {
+ checkPermission();
+ }
+
+ @Override
+ public void launchCamera() {
+ checkPermission();
+ }
+
+ @Override
+ public void onBootCompleted() {
+ checkPermission();
+ mKeyguardViewMediator.onBootCompleted();
+ }
+ };
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
new file mode 100644
index 0000000..bb39d36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -0,0 +1,1369 @@
+/*
+ * 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.keyguard;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.SearchManager;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.SoundPool;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Slog;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerPolicy;
+
+import com.android.internal.policy.IKeyguardExitCallback;
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardDisplayManager;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.MultiUserAvatarCache;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.keyguard.analytics.KeyguardAnalytics;
+import com.android.keyguard.analytics.Session;
+import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowManager;
+
+import java.io.File;
+
+import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
+import static com.android.keyguard.analytics.KeyguardAnalytics.SessionTypeAdapter;
+
+
+/**
+ * Mediates requests related to the keyguard. This includes queries about the
+ * state of the keyguard, power management events that effect whether the keyguard
+ * should be shown or reset, callbacks to the phone window manager to notify
+ * it of when the keyguard is showing, and events from the keyguard view itself
+ * stating that the keyguard was succesfully unlocked.
+ *
+ * Note that the keyguard view is shown when the screen is off (as appropriate)
+ * so that once the screen comes on, it will be ready immediately.
+ *
+ * Example queries about the keyguard:
+ * - is {movement, key} one that should wake the keygaurd?
+ * - is the keyguard showing?
+ * - are input events restricted due to the state of the keyguard?
+ *
+ * Callbacks to the phone window manager:
+ * - the keyguard is showing
+ *
+ * Example external events that translate to keyguard view changes:
+ * - screen turned off -> reset the keyguard, and show it so it will be ready
+ * next time the screen turns on
+ * - keyboard is slid open -> if the keyguard is not secure, hide it
+ *
+ * Events from the keyguard view:
+ * - user succesfully unlocked keyguard -> hide keyguard view, and no longer
+ * restrict input events.
+ *
+ * Note: in addition to normal power managment events that effect the state of
+ * whether the keyguard should be showing, external apps and services may request
+ * that the keyguard be disabled via {@link #setKeyguardEnabled(boolean)}. When
+ * false, this will override all other conditions for turning on the keyguard.
+ *
+ * Threading and synchronization:
+ * This class is created by the initialization routine of the {@link android.view.WindowManagerPolicy},
+ * and runs on its thread. The keyguard UI is created from that thread in the
+ * constructor of this class. The apis may be called from other threads, including the
+ * {@link com.android.server.input.InputManagerService}'s and {@link android.view.WindowManager}'s.
+ * Therefore, methods on this class are synchronized, and any action that is pointed
+ * directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
+ * thread of the keyguard.
+ */
+public class KeyguardViewMediator extends SystemUI {
+ private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
+ final static boolean DEBUG = false;
+ private static final boolean ENABLE_ANALYTICS = Build.IS_DEBUGGABLE;
+ private final static boolean DBG_WAKE = false;
+
+ private final static String TAG = "KeyguardViewMediator";
+
+ private static final String DELAYED_KEYGUARD_ACTION =
+ "com.android.internal.policy.impl.PhoneWindowManager.DELAYED_KEYGUARD";
+
+ // used for handler messages
+ private static final int SHOW = 2;
+ private static final int HIDE = 3;
+ private static final int RESET = 4;
+ private static final int VERIFY_UNLOCK = 5;
+ private static final int NOTIFY_SCREEN_OFF = 6;
+ private static final int NOTIFY_SCREEN_ON = 7;
+ private static final int KEYGUARD_DONE = 9;
+ private static final int KEYGUARD_DONE_DRAWING = 10;
+ private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
+ private static final int SET_OCCLUDED = 12;
+ private static final int KEYGUARD_TIMEOUT = 13;
+ private static final int DISMISS = 17;
+
+ /**
+ * The default amount of time we stay awake (used for all key input)
+ */
+ public static final int AWAKE_INTERVAL_DEFAULT_MS = 10000;
+
+ /**
+ * How long to wait after the screen turns off due to timeout before
+ * turning on the keyguard (i.e, the user has this much time to turn
+ * the screen back on without having to face the keyguard).
+ */
+ private static final int KEYGUARD_LOCK_AFTER_DELAY_DEFAULT = 5000;
+
+ /**
+ * How long we'll wait for the {@link ViewMediatorCallback#keyguardDoneDrawing()}
+ * callback before unblocking a call to {@link #setKeyguardEnabled(boolean)}
+ * that is reenabling the keyguard.
+ */
+ private static final int KEYGUARD_DONE_DRAWING_TIMEOUT_MS = 2000;
+
+ /**
+ * Allow the user to expand the status bar when the keyguard is engaged
+ * (without a pattern or password).
+ */
+ private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true;
+
+ /**
+ * Allow the user to expand the status bar when a SECURE keyguard is engaged
+ * and {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS} is set
+ * (private notifications will be masked).
+ */
+ private static final boolean ENABLE_SECURE_STATUS_BAR_EXPAND = true;
+
+ /**
+ * Default value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}.
+ */
+ private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false;
+
+ /**
+ * Secure setting whether analytics are collected on the keyguard.
+ */
+ private static final String KEYGUARD_ANALYTICS_SETTING = "keyguard_analytics";
+
+ /** The stream type that the lock sounds are tied to. */
+ private int mMasterStreamType;
+
+ private AlarmManager mAlarmManager;
+ private AudioManager mAudioManager;
+ private StatusBarManager mStatusBarManager;
+ private boolean mSwitchingUser;
+
+ private boolean mSystemReady;
+
+ // Whether the next call to playSounds() should be skipped. Defaults to
+ // true because the first lock (on boot) should be silent.
+ private boolean mSuppressNextLockSound = true;
+
+
+ /** High level access to the power manager for WakeLocks */
+ private PowerManager mPM;
+
+ /** UserManager for querying number of users */
+ private UserManager mUserManager;
+
+ /** SearchManager for determining whether or not search assistant is available */
+ private SearchManager mSearchManager;
+
+ /**
+ * Used to keep the device awake while to ensure the keyguard finishes opening before
+ * we sleep.
+ */
+ private PowerManager.WakeLock mShowKeyguardWakeLock;
+
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
+ private KeyguardAnalytics mKeyguardAnalytics;
+
+ // these are protected by synchronized (this)
+
+ /**
+ * External apps (like the phone app) can tell us to disable the keygaurd.
+ */
+ private boolean mExternallyEnabled = true;
+
+ /**
+ * Remember if an external call to {@link #setKeyguardEnabled} with value
+ * false caused us to hide the keyguard, so that we need to reshow it once
+ * the keygaurd is reenabled with another call with value true.
+ */
+ private boolean mNeedToReshowWhenReenabled = false;
+
+ // cached value of whether we are showing (need to know this to quickly
+ // answer whether the input should be restricted)
+ private boolean mShowing;
+
+ // true if the keyguard is hidden by another window
+ private boolean mOccluded = false;
+
+ /**
+ * Helps remember whether the screen has turned on since the last time
+ * it turned off due to timeout. see {@link #onScreenTurnedOff(int)}
+ */
+ private int mDelayedShowingSequence;
+
+ /**
+ * If the user has disabled the keyguard, then requests to exit, this is
+ * how we'll ultimately let them know whether it was successful. We use this
+ * var being non-null as an indicator that there is an in progress request.
+ */
+ private IKeyguardExitCallback mExitSecureCallback;
+
+ // the properties of the keyguard
+
+ private KeyguardUpdateMonitor mUpdateMonitor;
+
+ private boolean mScreenOn;
+
+ // last known state of the cellular connection
+ private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
+
+ /**
+ * we send this intent when the keyguard is dismissed.
+ */
+ private static final Intent USER_PRESENT_INTENT = new Intent(Intent.ACTION_USER_PRESENT)
+ .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ /**
+ * {@link #setKeyguardEnabled} waits on this condition when it reenables
+ * the keyguard.
+ */
+ private boolean mWaitingUntilKeyguardVisible = false;
+ private LockPatternUtils mLockPatternUtils;
+ private boolean mKeyguardDonePending = false;
+
+ private SoundPool mLockSounds;
+ private int mLockSoundId;
+ private int mUnlockSoundId;
+ private int mLockSoundStreamId;
+
+ /**
+ * Tracks value of {@link android.provider.Settings.Global#LOCK_SCREEN_SHOW_NOTIFICATIONS}.
+ */
+ private boolean mAllowNotificationsWhenSecure;
+
+ /**
+ * The volume applied to the lock/unlock sounds.
+ */
+ private float mLockSoundVolume;
+
+ /**
+ * For managing external displays
+ */
+ private KeyguardDisplayManager mKeyguardDisplayManager;
+
+ KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() {
+
+ @Override
+ public void onUserSwitching(int userId) {
+ // Note that the mLockPatternUtils user has already been updated from setCurrentUser.
+ // We need to force a reset of the views, since lockNow (called by
+ // ActivityManagerService) will not reconstruct the keyguard if it is already showing.
+ synchronized (KeyguardViewMediator.this) {
+ mSwitchingUser = true;
+ resetStateLocked();
+ adjustStatusBarLocked();
+ // When we switch users we want to bring the new user to the biometric unlock even
+ // if the current user has gone to the backup.
+ KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
+ }
+ }
+
+ @Override
+ public void onUserSwitchComplete(int userId) {
+ mSwitchingUser = false;
+ }
+
+ @Override
+ public void onUserRemoved(int userId) {
+ mLockPatternUtils.removeUser(userId);
+ MultiUserAvatarCache.getInstance().clear(userId);
+ }
+
+ @Override
+ public void onUserInfoChanged(int userId) {
+ MultiUserAvatarCache.getInstance().clear(userId);
+ }
+
+ @Override
+ public void onPhoneStateChanged(int phoneState) {
+ synchronized (KeyguardViewMediator.this) {
+ if (TelephonyManager.CALL_STATE_IDLE == phoneState // call ending
+ && !mScreenOn // screen off
+ && mExternallyEnabled) { // not disabled by any app
+
+ // note: this is a way to gracefully reenable the keyguard when the call
+ // ends and the screen is off without always reenabling the keyguard
+ // each time the screen turns off while in call (and having an occasional ugly
+ // flicker while turning back on the screen and disabling the keyguard again).
+ if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
+ + "keyguard is showing");
+ doKeyguardLocked(null);
+ }
+ }
+ }
+
+ @Override
+ public void onClockVisibilityChanged() {
+ adjustStatusBarLocked();
+ }
+
+ @Override
+ public void onDeviceProvisioned() {
+ sendUserPresentBroadcast();
+ }
+
+ @Override
+ public void onSimStateChanged(IccCardConstants.State simState) {
+ if (DEBUG) Log.d(TAG, "onSimStateChanged: " + simState);
+
+ switch (simState) {
+ case NOT_READY:
+ case ABSENT:
+ // only force lock screen in case of missing sim if user hasn't
+ // gone through setup wizard
+ synchronized (this) {
+ if (!mUpdateMonitor.isDeviceProvisioned()) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
+ + " we need to show the keyguard since the "
+ + "device isn't provisioned yet.");
+ doKeyguardLocked(null);
+ } else {
+ resetStateLocked();
+ }
+ }
+ }
+ break;
+ case PIN_REQUIRED:
+ case PUK_REQUIRED:
+ synchronized (this) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
+ + "showing; need to show keyguard so user can enter sim pin");
+ doKeyguardLocked(null);
+ } else {
+ resetStateLocked();
+ }
+ }
+ break;
+ case PERM_DISABLED:
+ synchronized (this) {
+ if (!isShowing()) {
+ if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
+ + "keygaurd isn't showing.");
+ doKeyguardLocked(null);
+ } else {
+ if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+ + "show permanently disabled message in lockscreen.");
+ resetStateLocked();
+ }
+ }
+ break;
+ case READY:
+ synchronized (this) {
+ if (isShowing()) {
+ resetStateLocked();
+ }
+ }
+ break;
+ }
+ }
+
+ };
+
+ ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
+
+ public void userActivity() {
+ KeyguardViewMediator.this.userActivity();
+ }
+
+ public void userActivity(long holdMs) {
+ KeyguardViewMediator.this.userActivity(holdMs);
+ }
+
+ public void keyguardDone(boolean authenticated) {
+ KeyguardViewMediator.this.keyguardDone(authenticated, true);
+ }
+
+ public void keyguardDoneDrawing() {
+ mHandler.sendEmptyMessage(KEYGUARD_DONE_DRAWING);
+ }
+
+ @Override
+ public void setNeedsInput(boolean needsInput) {
+ mStatusBarKeyguardViewManager.setNeedsInput(needsInput);
+ }
+
+ @Override
+ public void onUserActivityTimeoutChanged() {
+ mStatusBarKeyguardViewManager.updateUserActivityTimeout();
+ }
+
+ @Override
+ public void keyguardDonePending() {
+ mKeyguardDonePending = true;
+ }
+
+ @Override
+ public void keyguardGone() {
+ mKeyguardDisplayManager.hide();
+ }
+ };
+
+ private void userActivity() {
+ userActivity(AWAKE_INTERVAL_DEFAULT_MS);
+ }
+
+ public void userActivity(long holdMs) {
+ // We ignore the hold time. Eventually we should remove it.
+ // Instead, the keyguard window has an explicit user activity timeout set on it.
+ mPM.userActivity(SystemClock.uptimeMillis(), false);
+ }
+
+ private void setup() {
+ mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
+ mShowKeyguardWakeLock.setReferenceCounted(false);
+
+ mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
+
+ mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
+
+ mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+
+ mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+
+ mLockPatternUtils = new LockPatternUtils(mContext);
+ mLockPatternUtils.setCurrentUser(UserHandle.USER_OWNER);
+
+ // Assume keyguard is showing (unless it's disabled) until we know for sure...
+ mShowing = (mUpdateMonitor.isDeviceProvisioned() || mLockPatternUtils.isSecure())
+ && !mLockPatternUtils.isLockScreenDisabled();
+
+ mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(mContext,
+ mViewMediatorCallback, mLockPatternUtils);
+ final ContentResolver cr = mContext.getContentResolver();
+
+ if (ENABLE_ANALYTICS && !LockPatternUtils.isSafeModeEnabled() &&
+ Settings.Secure.getInt(cr, KEYGUARD_ANALYTICS_SETTING, 0) == 1) {
+ mKeyguardAnalytics = new KeyguardAnalytics(mContext, new SessionTypeAdapter() {
+
+ @Override
+ public int getSessionType() {
+ return mLockPatternUtils.isSecure() && !mUpdateMonitor.getUserHasTrust(
+ mLockPatternUtils.getCurrentUser())
+ ? Session.TYPE_KEYGUARD_SECURE
+ : Session.TYPE_KEYGUARD_INSECURE;
+ }
+ }, new File(mContext.getCacheDir(), "keyguard_analytics.bin"));
+ } else {
+ mKeyguardAnalytics = null;
+ }
+
+ mScreenOn = mPM.isScreenOn();
+
+ mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
+ String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND);
+ if (soundPath != null) {
+ mLockSoundId = mLockSounds.load(soundPath, 1);
+ }
+ if (soundPath == null || mLockSoundId == 0) {
+ Log.w(TAG, "failed to load lock sound from " + soundPath);
+ }
+ soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
+ if (soundPath != null) {
+ mUnlockSoundId = mLockSounds.load(soundPath, 1);
+ }
+ if (soundPath == null || mUnlockSoundId == 0) {
+ Log.w(TAG, "failed to load unlock sound from " + soundPath);
+ }
+ int lockSoundDefaultAttenuation = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_lockSoundVolumeDb);
+ mLockSoundVolume = (float)Math.pow(10, (float)lockSoundDefaultAttenuation/20);
+ }
+
+ @Override
+ public void start() {
+ setup();
+ putComponent(KeyguardViewMediator.class, this);
+ }
+
+ /**
+ * Let us know that the system is ready after startup.
+ */
+ public void onSystemReady() {
+ mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
+ synchronized (this) {
+ if (DEBUG) Log.d(TAG, "onSystemReady");
+ mSystemReady = true;
+ mUpdateMonitor.registerCallback(mUpdateCallback);
+
+ // Suppress biometric unlock right after boot until things have settled if it is the
+ // selected security method, otherwise unsuppress it. It must be unsuppressed if it is
+ // not the selected security method for the following reason: if the user starts
+ // without a screen lock selected, the biometric unlock would be suppressed the first
+ // time they try to use it.
+ //
+ // Note that the biometric unlock will still not show if it is not the selected method.
+ // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the
+ // selected method.
+ if (mLockPatternUtils.usingBiometricWeak()
+ && mLockPatternUtils.isBiometricWeakInstalled()) {
+ if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");
+ mUpdateMonitor.setAlternateUnlockEnabled(false);
+ } else {
+ mUpdateMonitor.setAlternateUnlockEnabled(true);
+ }
+
+ doKeyguardLocked(null);
+ }
+ // Most services aren't available until the system reaches the ready state, so we
+ // send it here when the device first boots.
+ maybeSendUserPresentBroadcast();
+ }
+
+ /**
+ * Called to let us know the screen was turned off.
+ * @param why either {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_USER},
+ * {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_TIMEOUT} or
+ * {@link android.view.WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
+ */
+ public void onScreenTurnedOff(int why) {
+ synchronized (this) {
+ mScreenOn = false;
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOff(" + why + ")");
+
+ mKeyguardDonePending = false;
+
+ // Lock immediately based on setting if secure (user has a pin/pattern/password).
+ // This also "locks" the device when not secure to provide easy access to the
+ // camera while preventing unwanted input.
+ final boolean lockImmediately =
+ mLockPatternUtils.getPowerButtonInstantlyLocks() || !mLockPatternUtils.isSecure();
+
+ if (mExitSecureCallback != null) {
+ if (DEBUG) Log.d(TAG, "pending exit secure callback cancelled");
+ try {
+ mExitSecureCallback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ mExitSecureCallback = null;
+ if (!mExternallyEnabled) {
+ hideLocked();
+ }
+ } else if (mShowing) {
+ notifyScreenOffLocked();
+ resetStateLocked();
+ } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_TIMEOUT
+ || (why == WindowManagerPolicy.OFF_BECAUSE_OF_USER && !lockImmediately)) {
+ doKeyguardLaterLocked();
+ } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
+ // Do not enable the keyguard if the prox sensor forced the screen off.
+ } else {
+ doKeyguardLocked(null);
+ }
+ if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) {
+ mKeyguardAnalytics.getCallback().onScreenOff();
+ }
+ }
+ KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurndOff(why);
+ }
+
+ private void doKeyguardLaterLocked() {
+ // if the screen turned off because of timeout or the user hit the power button
+ // and we don't need to lock immediately, set an alarm
+ // to enable it a little bit later (i.e, give the user a chance
+ // to turn the screen back on within a certain window without
+ // having to unlock the screen)
+ final ContentResolver cr = mContext.getContentResolver();
+
+ // From DisplaySettings
+ long displayTimeout = Settings.System.getInt(cr, SCREEN_OFF_TIMEOUT,
+ KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT);
+
+ // From SecuritySettings
+ final long lockAfterTimeout = Settings.Secure.getInt(cr,
+ Settings.Secure.LOCK_SCREEN_LOCK_AFTER_TIMEOUT,
+ KEYGUARD_LOCK_AFTER_DELAY_DEFAULT);
+
+ // From DevicePolicyAdmin
+ final long policyTimeout = mLockPatternUtils.getDevicePolicyManager()
+ .getMaximumTimeToLock(null, mLockPatternUtils.getCurrentUser());
+
+ long timeout;
+ if (policyTimeout > 0) {
+ // policy in effect. Make sure we don't go beyond policy limit.
+ displayTimeout = Math.max(displayTimeout, 0); // ignore negative values
+ timeout = Math.min(policyTimeout - displayTimeout, lockAfterTimeout);
+ } else {
+ timeout = lockAfterTimeout;
+ }
+
+ if (timeout <= 0) {
+ // Lock now
+ mSuppressNextLockSound = true;
+ doKeyguardLocked(null);
+ } else {
+ // Lock in the future
+ long when = SystemClock.elapsedRealtime() + timeout;
+ Intent intent = new Intent(DELAYED_KEYGUARD_ACTION);
+ intent.putExtra("seq", mDelayedShowingSequence);
+ PendingIntent sender = PendingIntent.getBroadcast(mContext,
+ 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, when, sender);
+ if (DEBUG) Log.d(TAG, "setting alarm to turn off keyguard, seq = "
+ + mDelayedShowingSequence);
+ }
+ }
+
+ private void cancelDoKeyguardLaterLocked() {
+ mDelayedShowingSequence++;
+ }
+
+ /**
+ * Let's us know the screen was turned on.
+ */
+ public void onScreenTurnedOn(IKeyguardShowCallback callback) {
+ synchronized (this) {
+ mScreenOn = true;
+ cancelDoKeyguardLaterLocked();
+ if (DEBUG) Log.d(TAG, "onScreenTurnedOn, seq = " + mDelayedShowingSequence);
+ if (callback != null) {
+ notifyScreenOnLocked(callback);
+ }
+ }
+ KeyguardUpdateMonitor.getInstance(mContext).dispatchScreenTurnedOn();
+ maybeSendUserPresentBroadcast();
+ }
+
+ private void maybeSendUserPresentBroadcast() {
+ if (mSystemReady && mLockPatternUtils.isLockScreenDisabled()
+ && !mUserManager.isUserSwitcherEnabled()) {
+ // Lock screen is disabled because the user has set the preference to "None".
+ // In this case, send out ACTION_USER_PRESENT here instead of in
+ // handleKeyguardDone()
+ sendUserPresentBroadcast();
+ }
+ }
+
+ /**
+ * A dream started. We should lock after the usual screen-off lock timeout but only
+ * if there is a secure lock pattern.
+ */
+ public void onDreamingStarted() {
+ synchronized (this) {
+ if (mScreenOn && mLockPatternUtils.isSecure()) {
+ doKeyguardLaterLocked();
+ }
+ }
+ }
+
+ /**
+ * A dream stopped.
+ */
+ public void onDreamingStopped() {
+ synchronized (this) {
+ if (mScreenOn) {
+ cancelDoKeyguardLaterLocked();
+ }
+ }
+ }
+
+ /**
+ * Same semantics as {@link android.view.WindowManagerPolicy#enableKeyguard}; provide
+ * a way for external stuff to override normal keyguard behavior. For instance
+ * the phone app disables the keyguard when it receives incoming calls.
+ */
+ public void setKeyguardEnabled(boolean enabled) {
+ synchronized (this) {
+ if (DEBUG) Log.d(TAG, "setKeyguardEnabled(" + enabled + ")");
+
+ mExternallyEnabled = enabled;
+
+ if (!enabled && mShowing) {
+ if (mExitSecureCallback != null) {
+ if (DEBUG) Log.d(TAG, "in process of verifyUnlock request, ignoring");
+ // we're in the process of handling a request to verify the user
+ // can get past the keyguard. ignore extraneous requests to disable / reenable
+ return;
+ }
+
+ // hiding keyguard that is showing, remember to reshow later
+ if (DEBUG) Log.d(TAG, "remembering to reshow, hiding keyguard, "
+ + "disabling status bar expansion");
+ mNeedToReshowWhenReenabled = true;
+ hideLocked();
+ } else if (enabled && mNeedToReshowWhenReenabled) {
+ // reenabled after previously hidden, reshow
+ if (DEBUG) Log.d(TAG, "previously hidden, reshowing, reenabling "
+ + "status bar expansion");
+ mNeedToReshowWhenReenabled = false;
+
+ if (mExitSecureCallback != null) {
+ if (DEBUG) Log.d(TAG, "onKeyguardExitResult(false), resetting");
+ try {
+ mExitSecureCallback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ mExitSecureCallback = null;
+ resetStateLocked();
+ } else {
+ showLocked(null);
+
+ // block until we know the keygaurd is done drawing (and post a message
+ // to unblock us after a timeout so we don't risk blocking too long
+ // and causing an ANR).
+ mWaitingUntilKeyguardVisible = true;
+ mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_DRAWING, KEYGUARD_DONE_DRAWING_TIMEOUT_MS);
+ if (DEBUG) Log.d(TAG, "waiting until mWaitingUntilKeyguardVisible is false");
+ while (mWaitingUntilKeyguardVisible) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ if (DEBUG) Log.d(TAG, "done waiting for mWaitingUntilKeyguardVisible");
+ }
+ }
+ }
+ }
+
+ /**
+ * @see android.app.KeyguardManager#exitKeyguardSecurely
+ */
+ public void verifyUnlock(IKeyguardExitCallback callback) {
+ synchronized (this) {
+ if (DEBUG) Log.d(TAG, "verifyUnlock");
+ if (!mUpdateMonitor.isDeviceProvisioned()) {
+ // don't allow this api when the device isn't provisioned
+ if (DEBUG) Log.d(TAG, "ignoring because device isn't provisioned");
+ try {
+ callback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ } else if (mExternallyEnabled) {
+ // this only applies when the user has externally disabled the
+ // keyguard. this is unexpected and means the user is not
+ // using the api properly.
+ Log.w(TAG, "verifyUnlock called when not externally disabled");
+ try {
+ callback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ } else if (mExitSecureCallback != null) {
+ // already in progress with someone else
+ try {
+ callback.onKeyguardExitResult(false);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(false)", e);
+ }
+ } else {
+ mExitSecureCallback = callback;
+ verifyUnlockLocked();
+ }
+ }
+ }
+
+ /**
+ * Is the keyguard currently showing?
+ */
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ public boolean isOccluded() {
+ return mOccluded;
+ }
+
+ /**
+ * Is the keyguard currently showing and not being force hidden?
+ */
+ public boolean isShowingAndNotOccluded() {
+ return mShowing && !mOccluded;
+ }
+
+ /**
+ * Notify us when the keyguard is occluded by another window
+ */
+ public void setOccluded(boolean isOccluded) {
+ if (DEBUG) Log.d(TAG, "setOccluded " + isOccluded);
+ mUpdateMonitor.sendKeyguardVisibilityChanged(!isOccluded);
+ mHandler.removeMessages(SET_OCCLUDED);
+ Message msg = mHandler.obtainMessage(SET_OCCLUDED, (isOccluded ? 1 : 0), 0);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Handles SET_OCCLUDED message sent by setOccluded()
+ */
+ private void handleSetOccluded(boolean isOccluded) {
+ synchronized (KeyguardViewMediator.this) {
+ if (mOccluded != isOccluded) {
+ mOccluded = isOccluded;
+ mStatusBarKeyguardViewManager.setOccluded(isOccluded);
+ updateActivityLockScreenState();
+ adjustStatusBarLocked();
+ }
+ if (ENABLE_ANALYTICS && mKeyguardAnalytics != null) {
+ mKeyguardAnalytics.getCallback().onSetOccluded(isOccluded);
+ }
+ }
+ }
+
+ /**
+ * Used by PhoneWindowManager to enable the keyguard due to a user activity timeout.
+ * This must be safe to call from any thread and with any window manager locks held.
+ */
+ public void doKeyguardTimeout(Bundle options) {
+ mHandler.removeMessages(KEYGUARD_TIMEOUT);
+ Message msg = mHandler.obtainMessage(KEYGUARD_TIMEOUT, options);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Given the state of the keyguard, is the input restricted?
+ * Input is restricted when the keyguard is showing, or when the keyguard
+ * was suppressed by an app that disabled the keyguard or we haven't been provisioned yet.
+ */
+ public boolean isInputRestricted() {
+ return mShowing || mNeedToReshowWhenReenabled || !mUpdateMonitor.isDeviceProvisioned();
+ }
+
+ /**
+ * Enable the keyguard if the settings are appropriate.
+ */
+ private void doKeyguardLocked(Bundle options) {
+ // if another app is disabling us, don't show
+ if (!mExternallyEnabled) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
+
+ // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
+ // for an occasional ugly flicker in this situation:
+ // 1) receive a call with the screen on (no keyguard) or make a call
+ // 2) screen times out
+ // 3) user hits key to turn screen back on
+ // instead, we reenable the keyguard when we know the screen is off and the call
+ // ends (see the broadcast receiver below)
+ // TODO: clean this up when we have better support at the window manager level
+ // for apps that wish to be on top of the keyguard
+ return;
+ }
+
+ // note whether notification access should be allowed
+ mAllowNotificationsWhenSecure = ENABLE_SECURE_STATUS_BAR_EXPAND
+ && 0 != Settings.Global.getInt(
+ mContext.getContentResolver(),
+ Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ ALLOW_NOTIFICATIONS_DEFAULT ? 1 : 0);
+
+ // if the keyguard is already showing, don't bother
+ if (mStatusBarKeyguardViewManager.isShowing()) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+ return;
+ }
+
+ // if the setup wizard hasn't run yet, don't show
+ final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
+ false);
+ final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
+ final IccCardConstants.State state = mUpdateMonitor.getSimState();
+ final boolean lockedOrMissing = state.isPinLocked()
+ || ((state == IccCardConstants.State.ABSENT
+ || state == IccCardConstants.State.PERM_DISABLED)
+ && requireSim);
+
+ if (!lockedOrMissing && !provisioned) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+ + " and the sim is not locked or missing");
+ return;
+ }
+
+ if (!mUserManager.isUserSwitcherEnabled()
+ && mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
+ if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
+ return;
+ }
+
+ if (mLockPatternUtils.checkVoldPassword()) {
+ if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
+ // Without this, settings is not enabled until the lock screen first appears
+ hideLocked();
+ return;
+ }
+
+ if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
+ showLocked(options);
+ }
+
+ /**
+ * Dismiss the keyguard through the security layers.
+ */
+ public void handleDismiss() {
+ if (mShowing && !mOccluded) {
+ mStatusBarKeyguardViewManager.dismiss();
+ }
+ }
+
+ public void dismiss() {
+ mHandler.sendEmptyMessage(DISMISS);
+ }
+
+ /**
+ * Send message to keyguard telling it to reset its state.
+ * @see #handleReset
+ */
+ private void resetStateLocked() {
+ if (DEBUG) Log.e(TAG, "resetStateLocked");
+ Message msg = mHandler.obtainMessage(RESET);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Send message to keyguard telling it to verify unlock
+ * @see #handleVerifyUnlock()
+ */
+ private void verifyUnlockLocked() {
+ if (DEBUG) Log.d(TAG, "verifyUnlockLocked");
+ mHandler.sendEmptyMessage(VERIFY_UNLOCK);
+ }
+
+
+ /**
+ * Send a message to keyguard telling it the screen just turned on.
+ * @see #onScreenTurnedOff(int)
+ * @see #handleNotifyScreenOff
+ */
+ private void notifyScreenOffLocked() {
+ if (DEBUG) Log.d(TAG, "notifyScreenOffLocked");
+ mHandler.sendEmptyMessage(NOTIFY_SCREEN_OFF);
+ }
+
+ /**
+ * Send a message to keyguard telling it the screen just turned on.
+ * @see #onScreenTurnedOn
+ * @see #handleNotifyScreenOn
+ */
+ private void notifyScreenOnLocked(IKeyguardShowCallback result) {
+ if (DEBUG) Log.d(TAG, "notifyScreenOnLocked");
+ Message msg = mHandler.obtainMessage(NOTIFY_SCREEN_ON, result);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Send message to keyguard telling it to show itself
+ * @see #handleShow
+ */
+ private void showLocked(Bundle options) {
+ if (DEBUG) Log.d(TAG, "showLocked");
+ // ensure we stay awake until we are finished displaying the keyguard
+ mShowKeyguardWakeLock.acquire();
+ Message msg = mHandler.obtainMessage(SHOW, options);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Send message to keyguard telling it to hide itself
+ * @see #handleHide()
+ */
+ private void hideLocked() {
+ if (DEBUG) Log.d(TAG, "hideLocked");
+ Message msg = mHandler.obtainMessage(HIDE);
+ mHandler.sendMessage(msg);
+ }
+
+ public boolean isSecure() {
+ return mLockPatternUtils.isSecure()
+ || KeyguardUpdateMonitor.getInstance(mContext).isSimPinSecure();
+ }
+
+ /**
+ * Update the newUserId. Call while holding WindowManagerService lock.
+ * NOTE: Should only be called by KeyguardViewMediator in response to the user id changing.
+ *
+ * @param newUserId The id of the incoming user.
+ */
+ public void setCurrentUser(int newUserId) {
+ mLockPatternUtils.setCurrentUser(newUserId);
+ }
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DELAYED_KEYGUARD_ACTION.equals(intent.getAction())) {
+ final int sequence = intent.getIntExtra("seq", 0);
+ if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
+ + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
+ synchronized (KeyguardViewMediator.this) {
+ if (mDelayedShowingSequence == sequence) {
+ // Don't play lockscreen SFX if the screen went off due to timeout.
+ mSuppressNextLockSound = true;
+ doKeyguardLocked(null);
+ }
+ }
+ }
+ }
+ };
+
+ public void keyguardDone(boolean authenticated, boolean wakeup) {
+ if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")");
+ EventLog.writeEvent(70000, 2);
+ synchronized (this) {
+ mKeyguardDonePending = false;
+ }
+ Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0, wakeup ? 1 : 0);
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * This handler will be associated with the policy thread, which will also
+ * be the UI thread of the keyguard. Since the apis of the policy, and therefore
+ * this class, can be called by other threads, any action that directly
+ * interacts with the keyguard ui should be posted to this handler, rather
+ * than called directly.
+ */
+ private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case SHOW:
+ handleShow((Bundle) msg.obj);
+ break;
+ case HIDE:
+ handleHide();
+ break;
+ case RESET:
+ handleReset();
+ break;
+ case VERIFY_UNLOCK:
+ handleVerifyUnlock();
+ break;
+ case NOTIFY_SCREEN_OFF:
+ handleNotifyScreenOff();
+ break;
+ case NOTIFY_SCREEN_ON:
+ handleNotifyScreenOn((IKeyguardShowCallback) msg.obj);
+ break;
+ case KEYGUARD_DONE:
+ handleKeyguardDone(msg.arg1 != 0, msg.arg2 != 0);
+ break;
+ case KEYGUARD_DONE_DRAWING:
+ handleKeyguardDoneDrawing();
+ break;
+ case KEYGUARD_DONE_AUTHENTICATING:
+ keyguardDone(true, true);
+ break;
+ case SET_OCCLUDED:
+ handleSetOccluded(msg.arg1 != 0);
+ break;
+ case KEYGUARD_TIMEOUT:
+ synchronized (KeyguardViewMediator.this) {
+ doKeyguardLocked((Bundle) msg.obj);
+ }
+ break;
+ case DISMISS:
+ handleDismiss();
+ break;
+ }
+ }
+ };
+
+ /**
+ * @see #keyguardDone
+ * @see #KEYGUARD_DONE
+ */
+ private void handleKeyguardDone(boolean authenticated, boolean wakeup) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardDone");
+
+ if (authenticated) {
+ mUpdateMonitor.clearFailedUnlockAttempts();
+ }
+
+ if (mExitSecureCallback != null) {
+ try {
+ mExitSecureCallback.onKeyguardExitResult(authenticated);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to call onKeyguardExitResult(" + authenticated + ")", e);
+ }
+
+ mExitSecureCallback = null;
+
+ if (authenticated) {
+ // after succesfully exiting securely, no need to reshow
+ // the keyguard when they've released the lock
+ mExternallyEnabled = true;
+ mNeedToReshowWhenReenabled = false;
+ }
+ }
+
+ handleHide();
+ sendUserPresentBroadcast();
+ }
+
+ private void sendUserPresentBroadcast() {
+ final UserHandle currentUser = new UserHandle(mLockPatternUtils.getCurrentUser());
+ mContext.sendBroadcastAsUser(USER_PRESENT_INTENT, currentUser);
+ }
+
+ /**
+ * @see #keyguardDone
+ * @see #KEYGUARD_DONE_DRAWING
+ */
+ private void handleKeyguardDoneDrawing() {
+ synchronized(this) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing");
+ if (mWaitingUntilKeyguardVisible) {
+ if (DEBUG) Log.d(TAG, "handleKeyguardDoneDrawing: notifying mWaitingUntilKeyguardVisible");
+ mWaitingUntilKeyguardVisible = false;
+ notifyAll();
+
+ // there will usually be two of these sent, one as a timeout, and one
+ // as a result of the callback, so remove any remaining messages from
+ // the queue
+ mHandler.removeMessages(KEYGUARD_DONE_DRAWING);
+ }
+ }
+ }
+
+ private void playSounds(boolean locked) {
+ // User feedback for keyguard.
+
+ if (mSuppressNextLockSound) {
+ mSuppressNextLockSound = false;
+ return;
+ }
+
+ final ContentResolver cr = mContext.getContentResolver();
+ if (Settings.System.getInt(cr, Settings.System.LOCKSCREEN_SOUNDS_ENABLED, 1) == 1) {
+ final int whichSound = locked
+ ? mLockSoundId
+ : mUnlockSoundId;
+ mLockSounds.stop(mLockSoundStreamId);
+ // Init mAudioManager
+ if (mAudioManager == null) {
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ if (mAudioManager == null) return;
+ mMasterStreamType = mAudioManager.getMasterStreamType();
+ }
+ // If the stream is muted, don't play the sound
+ if (mAudioManager.isStreamMute(mMasterStreamType)) return;
+
+ mLockSoundStreamId = mLockSounds.play(whichSound,
+ mLockSoundVolume, mLockSoundVolume, 1/*priortiy*/, 0/*loop*/, 1.0f/*rate*/);
+ }
+ }
+
+ private void updateActivityLockScreenState() {
+ try {
+ ActivityManagerNative.getDefault().setLockScreenShown(mShowing && !mOccluded);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #showLocked}.
+ * @see #SHOW
+ */
+ private void handleShow(Bundle options) {
+ synchronized (KeyguardViewMediator.this) {
+ if (!mSystemReady) {
+ if (DEBUG) Log.d(TAG, "ignoring handleShow because system is not ready.");
+ return;
+ } else {
+ if (DEBUG) Log.d(TAG, "handleShow");
+ }
+
+ mStatusBarKeyguardViewManager.show(options);
+ mShowing = true;
+ mKeyguardDonePending = false;
+ updateActivityLockScreenState();
+ adjustStatusBarLocked();
+ userActivity();
+
+ // Do this at the end to not slow down display of the keyguard.
+ playSounds(true);
+
+ mShowKeyguardWakeLock.release();
+ }
+ mKeyguardDisplayManager.show();
+ }
+
+ /**
+ * Handle message sent by {@link #hideLocked()}
+ * @see #HIDE
+ */
+ private void handleHide() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleHide");
+
+ // only play "unlock" noises if not on a call (since the incall UI
+ // disables the keyguard)
+ if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
+ playSounds(false);
+ }
+
+ mStatusBarKeyguardViewManager.hide();
+ mShowing = false;
+ mKeyguardDonePending = false;
+ updateActivityLockScreenState();
+ adjustStatusBarLocked();
+ }
+ }
+
+ private void adjustStatusBarLocked() {
+ if (mStatusBarManager == null) {
+ mStatusBarManager = (StatusBarManager)
+ mContext.getSystemService(Context.STATUS_BAR_SERVICE);
+ }
+ if (mStatusBarManager == null) {
+ Log.w(TAG, "Could not get status bar manager");
+ } else {
+ // Disable aspects of the system/status/navigation bars that must not be re-enabled by
+ // windows that appear on top, ever
+ int flags = StatusBarManager.DISABLE_NONE;
+ if (mShowing) {
+ // Permanently disable components not available when keyguard is enabled
+ // (like recents). Temporary enable/disable (e.g. the "back" button) are
+ // done in KeyguardHostView.
+ flags |= StatusBarManager.DISABLE_RECENT;
+ if ((isSecure() && !mAllowNotificationsWhenSecure)
+ || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
+ // showing secure lockscreen; disable expanding.
+ flags |= StatusBarManager.DISABLE_EXPAND;
+ }
+ if (isSecure()) {
+ // showing secure lockscreen; disable ticker and switch private notifications
+ // to show their public versions, if available.
+ flags |= StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS;
+ }
+ if (!isAssistantAvailable()) {
+ flags |= StatusBarManager.DISABLE_SEARCH;
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
+ + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
+ }
+
+ if (!(mContext instanceof Activity)) {
+ mStatusBarManager.disable(flags);
+ }
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #resetStateLocked}
+ * @see #RESET
+ */
+ private void handleReset() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleReset");
+ mStatusBarKeyguardViewManager.reset();
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #verifyUnlock}
+ * @see #VERIFY_UNLOCK
+ */
+ private void handleVerifyUnlock() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleVerifyUnlock");
+ mStatusBarKeyguardViewManager.verifyUnlock();
+ mShowing = true;
+ updateActivityLockScreenState();
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #notifyScreenOffLocked()}
+ * @see #NOTIFY_SCREEN_OFF
+ */
+ private void handleNotifyScreenOff() {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleNotifyScreenOff");
+ mStatusBarKeyguardViewManager.onScreenTurnedOff();
+ }
+ }
+
+ /**
+ * Handle message sent by {@link #notifyScreenOnLocked}
+ * @see #NOTIFY_SCREEN_ON
+ */
+ private void handleNotifyScreenOn(IKeyguardShowCallback callback) {
+ synchronized (KeyguardViewMediator.this) {
+ if (DEBUG) Log.d(TAG, "handleNotifyScreenOn");
+ mStatusBarKeyguardViewManager.onScreenTurnedOn(callback);
+ }
+ }
+
+ public boolean isDismissable() {
+ return mKeyguardDonePending || !isSecure();
+ }
+
+ private boolean isAssistantAvailable() {
+ return mSearchManager != null
+ && mSearchManager.getAssistIntent(mContext, false, UserHandle.USER_CURRENT) != null;
+ }
+
+ public void onBootCompleted() {
+ mUpdateMonitor.dispatchBootCompleted();
+ }
+
+ public StatusBarKeyguardViewManager registerStatusBar(PhoneStatusBar phoneStatusBar,
+ ViewGroup container, StatusBarWindowManager statusBarWindowManager) {
+ mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container,
+ statusBarWindowManager);
+ return mStatusBarKeyguardViewManager;
+ }
+
+ public ViewMediatorCallback getViewMediatorCallback() {
+ return mViewMediatorCallback;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
new file mode 100644
index 0000000..87ebcc1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserSwitcherHostView.java
@@ -0,0 +1,172 @@
+/*
+ * 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.settings;
+
+import com.android.systemui.R;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManagerGlobal;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * A quick and dirty view to show a user switcher.
+ */
+public class UserSwitcherHostView extends FrameLayout implements ListView.OnItemClickListener {
+
+ private static final String TAG = "UserSwitcherDialog";
+
+ private ArrayList<UserInfo> mUserInfo = new ArrayList<UserInfo>();
+ private Adapter mAdapter = new Adapter();
+ private UserManager mUserManager;
+ private Runnable mFinishRunnable;
+ private ListView mListView;
+
+ public UserSwitcherHostView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ if (isInEditMode()) {
+ return;
+ }
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ }
+
+ public UserSwitcherHostView(Context context, AttributeSet attrs) {
+ this(context, attrs, com.android.internal.R.attr.listViewStyle);
+ }
+
+ public UserSwitcherHostView(Context context) {
+ this(context, null);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mListView = (ListView) findViewById(android.R.id.list);
+ mListView.setAdapter(mAdapter);
+ mListView.setOnItemClickListener(this);
+ }
+
+ @Override
+ public void onItemClick(AdapterView<?> l, View v, int position, long id) {
+ int userId = mAdapter.getItem(position).id;
+ try {
+ WindowManagerGlobal.getWindowManagerService().lockNow(null);
+ ActivityManagerNative.getDefault().switchUser(userId);
+ finish();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't switch user.", e);
+ }
+ }
+
+ private void finish() {
+ if (mFinishRunnable != null) {
+ mFinishRunnable.run();
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ finish();
+ }
+ return true;
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ // A gross hack to get rid of the switcher when the shade is collapsed.
+ if (visibility != VISIBLE) {
+ finish();
+ }
+ }
+
+ public void setFinishRunnable(Runnable finishRunnable) {
+ mFinishRunnable = finishRunnable;
+ }
+
+ public void refreshUsers() {
+ mUserInfo.clear();
+ mUserInfo.addAll(mUserManager.getUsers(true));
+ mAdapter.notifyDataSetChanged();
+ }
+
+ private class Adapter extends BaseAdapter {
+
+ @Override
+ public int getCount() {
+ return mUserInfo.size();
+ }
+
+ @Override
+ public UserInfo getItem(int position) {
+ return mUserInfo.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getItem(position).serialNumber;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null || (!(convertView.getTag() instanceof ViewHolder))) {
+ convertView = createView(parent);
+ }
+ ViewHolder h = (ViewHolder) convertView.getTag();
+ bindView(h, getItem(position));
+ return convertView;
+ }
+
+ private View createView(ViewGroup parent) {
+ View v = LayoutInflater.from(getContext()).inflate(
+ R.layout.user_switcher_item, parent, false);
+ ViewHolder h = new ViewHolder();
+ h.name = (TextView) v.findViewById(R.id.user_name);
+ h.picture = (ImageView) v.findViewById(R.id.user_picture);
+ v.setTag(h);
+ return v;
+ }
+
+ private void bindView(ViewHolder h, UserInfo item) {
+ h.name.setText(item.name);
+ h.picture.setImageBitmap(mUserManager.getUserIcon(item.id));
+ }
+
+ class ViewHolder {
+ TextView name;
+ ImageView picture;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 2f135ec..e51b914 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -168,9 +168,9 @@ public abstract class BaseStatusBar extends SystemUI implements
protected int mZenMode;
- public IStatusBarService getStatusBarService() {
- return mBarService;
- }
+ protected boolean mOnKeyguard;
+ protected View mKeyguardIconOverflowContainer;
+ protected NotificationOverflowIconsView mOverflowIconsView;
public boolean isDeviceProvisioned() {
return mDeviceProvisioned;
@@ -435,8 +435,6 @@ public abstract class BaseStatusBar extends SystemUI implements
}
if (version > 0 && version < Build.VERSION_CODES.GINGERBREAD) {
content.setBackgroundResource(R.drawable.notification_row_legacy_bg);
- } else {
- content.setBackgroundResource(com.android.internal.R.drawable.notification_bg);
}
}
}
@@ -1004,7 +1002,7 @@ public abstract class BaseStatusBar extends SystemUI implements
// Remove the expanded view.
ViewGroup rowParent = (ViewGroup)entry.row.getParent();
if (rowParent != null) rowParent.removeView(entry.row);
- updateExpansionStates();
+ updateRowStates();
updateNotificationIcons();
return entry.notification;
@@ -1047,7 +1045,7 @@ public abstract class BaseStatusBar extends SystemUI implements
if (DEBUG) {
Log.d(TAG, "addNotificationViews: added at " + pos);
}
- updateExpansionStates();
+ updateRowStates();
updateNotificationIcons();
}
@@ -1055,28 +1053,53 @@ public abstract class BaseStatusBar extends SystemUI implements
addNotificationViews(createNotificationViews(key, notification));
}
- protected void updateExpansionStates() {
+ /**
+ * @return The number of notifications we show on Keyguard.
+ */
+ protected abstract int getMaxKeyguardNotifications();
- // TODO: Handle user expansion better
- int N = mNotificationData.size();
- for (int i = 0; i < N; i++) {
+ /**
+ * Updates expanded, dimmed and locked states of notification rows.
+ */
+ protected void updateRowStates() {
+ int maxKeyguardNotifications = getMaxKeyguardNotifications();
+ mOverflowIconsView.removeAllViews();
+ int n = mNotificationData.size();
+ int visibleNotifications = 0;
+ for (int i = n-1; i >= 0; i--) {
NotificationData.Entry entry = mNotificationData.get(i);
- if (!entry.row.isUserLocked()) {
- if (i == (N-1)) {
- if (DEBUG) Log.d(TAG, "expanding top notification at " + i);
- entry.row.setSystemExpanded(true);
- } else {
- if (!entry.row.isUserExpanded()) {
- if (DEBUG) Log.d(TAG, "collapsing notification at " + i);
- entry.row.setSystemExpanded(false);
- } else {
- if (DEBUG) Log.d(TAG, "ignoring user-modified notification at " + i);
- }
+ if (mOnKeyguard) {
+ entry.row.setSystemExpanded(false);
+ } else {
+ if (!entry.row.isUserLocked()) {
+ boolean top = (i == n-1);
+ entry.row.setSystemExpanded(top || entry.row.isUserExpanded());
+ }
+ }
+ entry.row.setDimmed(mOnKeyguard);
+ entry.row.setLocked(mOnKeyguard);
+ boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
+ if (mOnKeyguard && (visibleNotifications >= maxKeyguardNotifications
+ || !showOnKeyguard)) {
+ entry.row.setVisibility(View.GONE);
+ if (showOnKeyguard) {
+ mOverflowIconsView.addNotification(entry);
}
} else {
- if (DEBUG) Log.d(TAG, "ignoring notification being held by user at " + i);
+ entry.row.setVisibility(View.VISIBLE);
+ visibleNotifications++;
}
}
+
+ if (mOnKeyguard && mOverflowIconsView.getChildCount() > 0) {
+ mKeyguardIconOverflowContainer.setVisibility(View.VISIBLE);
+ } else {
+ mKeyguardIconOverflowContainer.setVisibility(View.GONE);
+ }
+ }
+
+ private boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
+ return sbn.getNotification().priority >= Notification.PRIORITY_LOW;
}
protected void setZenMode(int mode) {
@@ -1208,7 +1231,7 @@ public abstract class BaseStatusBar extends SystemUI implements
handleNotificationError(key, notification, "Couldn't update icon: " + ic);
return;
}
- updateExpansionStates();
+ updateRowStates();
}
catch (RuntimeException e) {
// It failed to add cleanly. Log, and remove the view from the panel.
@@ -1315,7 +1338,7 @@ public abstract class BaseStatusBar extends SystemUI implements
boolean interrupt = (isFullscreen || (isHighPriority && (isNoisy || hasTicker)))
&& isAllowed
&& mPowerManager.isScreenOn()
- && !keyguard.isShowingAndNotHidden()
+ && !keyguard.isShowingAndNotOccluded()
&& !keyguard.isInputRestricted();
try {
interrupt = interrupt && !mDreamManager.isDreaming();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 39333d7..bbbe8fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -21,6 +21,7 @@ import android.os.IBinder;
import android.os.Message;
import android.service.notification.StatusBarNotification;
+import com.android.internal.policy.IKeyguardShowCallback;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
@@ -98,6 +99,7 @@ public class CommandQueue extends IStatusBar.Stub {
public void hideSearchPanel();
public void cancelPreloadRecentApps();
public void setWindowState(int window, int state);
+
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -232,6 +234,7 @@ public class CommandQueue extends IStatusBar.Stub {
}
}
+
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
@@ -295,7 +298,7 @@ public class CommandQueue extends IStatusBar.Stub {
mCallbacks.topAppWindowChanged(msg.arg1 != 0);
break;
case MSG_SHOW_IME_BUTTON:
- mCallbacks.setImeWindowStatus((IBinder)msg.obj, msg.arg1, msg.arg2);
+ mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2);
break;
case MSG_SET_HARD_KEYBOARD_STATUS:
mCallbacks.setHardKeyboardStatus(msg.arg1 != 0, msg.arg2 != 0);
@@ -312,6 +315,7 @@ public class CommandQueue extends IStatusBar.Stub {
case MSG_SET_WINDOW_STATE:
mCallbacks.setWindowState(msg.arg1, msg.arg2);
break;
+
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 2daf619..7bacc13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -40,6 +40,8 @@ public class ExpandableNotificationRow extends FrameLayout {
/** Are we showing the "public" version */
private boolean mShowingPublic;
+ private LatestItemView mLatestItemView;
+
/**
* Is this notification expanded by the system. The expansion state can be overridden by the
* user expansion.
@@ -59,8 +61,10 @@ public class ExpandableNotificationRow extends FrameLayout {
super.onFinishInflate();
mPublicLayout = (SizeAdaptiveLayout) findViewById(R.id.expandedPublic);
mPrivateLayout = (SizeAdaptiveLayout) findViewById(R.id.expanded);
+ mLatestItemView = (LatestItemView) findViewById(R.id.container);
}
+
public void setHeightRange(int rowMinHeight, int rowMaxHeight) {
mRowMinHeight = rowMinHeight;
mRowMaxHeight = rowMaxHeight;
@@ -152,8 +156,6 @@ public class ExpandableNotificationRow extends FrameLayout {
return mShowingPublic ? mRowMinHeight : getMaxExpandHeight();
}
-
-
private void updateMaxExpandHeight() {
ViewGroup.LayoutParams lp = getLayoutParams();
int oldHeight = lp.height;
@@ -195,6 +197,13 @@ public class ExpandableNotificationRow extends FrameLayout {
mPrivateLayout.setVisibility(show ? View.GONE : View.VISIBLE);
}
+ /**
+ * Sets the notification as dimmed, meaning that it will appear in a more gray variant.
+ */
+ public void setDimmed(boolean dimmed) {
+ mLatestItemView.setDimmed(dimmed);
+ }
+
public int getMaxExpandHeight() {
if (mMaxHeightNeedsUpdate) {
updateMaxExpandHeight();
@@ -202,4 +211,12 @@ public class ExpandableNotificationRow extends FrameLayout {
}
return mMaxExpandHeight;
}
+
+ /**
+ * Sets the notification as locked. In the locked state, the first tap will produce a quantum
+ * ripple to make the notification brighter and only the second tap will cause a click.
+ */
+ public void setLocked(boolean locked) {
+ mLatestItemView.setLocked(locked);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
index 6419777..ad9028d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java
@@ -17,16 +17,44 @@
package com.android.systemui.statusbar;
import android.content.Context;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
public class LatestItemView extends FrameLayout {
+
+ private static final long DOUBLETAP_TIMEOUT_MS = 1000;
+
+ private boolean mDimmed;
+ private boolean mLocked;
+
+ /**
+ * Flag to indicate that the notification has been touched once and the second touch will
+ * click it.
+ */
+ private boolean mActivated;
+
+ private float mDownX;
+ private float mDownY;
+ private final float mTouchSlop;
+ private boolean mHotspotActive;
+
public LatestItemView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
+ private final Runnable mTapTimeoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ makeInactive();
+ }
+ };
+
@Override
public void setOnClickListener(OnClickListener l) {
super.setOnClickListener(l);
@@ -45,4 +73,94 @@ public class LatestItemView extends FrameLayout {
}
return false;
}
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mLocked) {
+ return handleTouchEventLocked(event);
+ } else {
+ return super.onTouchEvent(event);
+ }
+ }
+
+ private boolean handleTouchEventLocked(MotionEvent event) {
+ int action = event.getActionMasked();
+ Drawable background = getBackground();
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mDownX = event.getX();
+ mDownY = event.getY();
+ if (!mActivated) {
+ background.setHotspot(0, event.getX(), event.getY());
+ mHotspotActive = true;
+ }
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (!isWithinTouchSlop(event)) {
+ makeInactive();
+ return false;
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ if (isWithinTouchSlop(event)) {
+ if (!mActivated) {
+ mActivated = true;
+ postDelayed(mTapTimeoutRunnable, DOUBLETAP_TIMEOUT_MS);
+ } else {
+ performClick();
+ makeInactive();
+ }
+ } else {
+ makeInactive();
+ }
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ makeInactive();
+ break;
+ default:
+ break;
+ }
+ return true;
+ }
+
+ /**
+ * Cancels the hotspot and makes the notification inactive.
+ */
+ private void makeInactive() {
+ if (mHotspotActive) {
+ // Make sure that we clear the hotspot from the center.
+ getBackground().setHotspot(0, getWidth()/2, getHeight()/2);
+ getBackground().removeHotspot(0);
+ mHotspotActive = false;
+ }
+ mActivated = false;
+ removeCallbacks(mTapTimeoutRunnable);
+ }
+
+ private boolean isWithinTouchSlop(MotionEvent event) {
+ return Math.abs(event.getX() - mDownX) < mTouchSlop
+ && Math.abs(event.getY() - mDownY) < mTouchSlop;
+ }
+
+ /**
+ * Sets the notification as dimmed, meaning that it will appear in a more gray variant.
+ */
+ public void setDimmed(boolean dimmed) {
+ if (mDimmed != dimmed) {
+ mDimmed = dimmed;
+ if (dimmed) {
+ setBackgroundResource(com.android.internal.R.drawable.notification_quantum_bg_dim);
+ } else {
+ setBackgroundResource(com.android.internal.R.drawable.notification_quantum_bg);
+ }
+ }
+ }
+
+ /**
+ * Sets the notification as locked. In the locked state, the first tap will produce a quantum
+ * ripple to make the notification brighter and only the second tap will cause a click.
+ */
+ public void setLocked(boolean locked) {
+ mLocked = locked;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java
new file mode 100644
index 0000000..ce31894
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationOverflowIconsView.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+import android.app.Notification;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.IconMerger;
+
+/**
+ * A view to display all the overflowing icons on Keyguard.
+ */
+public class NotificationOverflowIconsView extends IconMerger {
+
+ private TextView mMoreText;
+ private int mTintColor;
+
+ public NotificationOverflowIconsView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mTintColor = getResources().getColor(R.color.keyguard_overflow_content_color);
+ }
+
+ public void setMoreText(TextView moreText) {
+ mMoreText = moreText;
+ }
+
+ public void addNotification(NotificationData.Entry notification) {
+ StatusBarIconView v = new StatusBarIconView(getContext(), "",
+ notification.notification.getNotification());
+ v.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ v.setColorFilter(mTintColor, PorterDuff.Mode.MULTIPLY);
+ addView(v);
+ v.set(notification.icon.getStatusBarIcon());
+ updateMoreText();
+ }
+
+ private void updateMoreText() {
+ mMoreText.setText(getResources().getQuantityString(
+ R.plurals.keyguard_more_overflow_text, getChildCount(), getChildCount()));
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
new file mode 100644
index 0000000..7cbde36
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -0,0 +1,139 @@
+/*
+ * 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.phone;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardHostView;
+import com.android.keyguard.KeyguardViewBase;
+import com.android.keyguard.R;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+
+/**
+ * A class which manages the bouncer on the lockscreen.
+ */
+public class KeyguardBouncer {
+
+ private Context mContext;
+ private ViewMediatorCallback mCallback;
+ private LockPatternUtils mLockPatternUtils;
+ private ViewGroup mContainer;
+ private StatusBarWindowManager mWindowManager;
+ private KeyguardViewBase mKeyguardView;
+ private ViewGroup mRoot;
+
+ public KeyguardBouncer(Context context, ViewMediatorCallback callback,
+ LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
+ ViewGroup container) {
+ mContext = context;
+ mCallback = callback;
+ mLockPatternUtils = lockPatternUtils;
+ mContainer = container;
+ mWindowManager = windowManager;
+ }
+
+ public void prepare() {
+ ensureView();
+ }
+
+ public void show() {
+ ensureView();
+
+ // Try to dismiss the Keyguard. If no security pattern is set, this will dismiss the whole
+ // Keyguard. If we need to authenticate, show the bouncer.
+ if (!mKeyguardView.dismiss()) {
+ mRoot.setVisibility(View.VISIBLE);
+ mKeyguardView.requestFocus();
+ }
+ }
+
+ public void hide() {
+ if (mKeyguardView != null) {
+ mKeyguardView.cleanUp();
+ }
+ removeView();
+ }
+
+ /**
+ * Reset the state of the view.
+ */
+ public void reset() {
+ inflateView();
+ }
+
+ public void onScreenTurnedOff() {
+ if (mKeyguardView != null) {
+ mKeyguardView.onScreenTurnedOff();
+ }
+ }
+
+ public void onScreenTurnedOn() {
+ if (mKeyguardView != null) {
+ mKeyguardView.onScreenTurnedOn();
+ }
+ }
+
+ public long getUserActivityTimeout() {
+ if (mKeyguardView != null) {
+ long timeout = mKeyguardView.getUserActivityTimeout();
+ if (timeout >= 0) {
+ return timeout;
+ }
+ }
+ return KeyguardViewMediator.AWAKE_INTERVAL_DEFAULT_MS;
+ }
+
+ public boolean isShowing() {
+ return mRoot != null && mRoot.getVisibility() == View.VISIBLE;
+ }
+
+ private void ensureView() {
+ if (mRoot == null) {
+ inflateView();
+ }
+ }
+
+ private void inflateView() {
+ removeView();
+ mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
+ mKeyguardView = (KeyguardViewBase) mRoot.findViewById(R.id.keyguard_host_view);
+ mKeyguardView.setLockPatternUtils(mLockPatternUtils);
+ mKeyguardView.setViewMediatorCallback(mCallback);
+ mContainer.addView(mRoot, mContainer.getChildCount());
+ mRoot.setVisibility(View.INVISIBLE);
+ mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
+ }
+
+ private void removeView() {
+ if (mRoot != null && mRoot.getParent() == mContainer) {
+ mContainer.removeView(mRoot);
+ mRoot = null;
+ }
+ }
+
+ public boolean onBackPressed() {
+ return mKeyguardView != null && mKeyguardView.handleBackKey();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
index 1ea920d..754075a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardTouchDelegate.java
@@ -39,8 +39,8 @@ import java.util.List;
*/
public class KeyguardTouchDelegate {
// TODO: propagate changes to these to {@link KeyguardServiceDelegate}
- static final String KEYGUARD_PACKAGE = "com.android.keyguard";
- static final String KEYGUARD_CLASS = "com.android.keyguard.KeyguardService";
+ static final String KEYGUARD_PACKAGE = "com.android.systemui";
+ static final String KEYGUARD_CLASS = "com.android.systemui.keyguard.KeyguardService";
private static KeyguardTouchDelegate sInstance;
private static final List<OnKeyguardConnectionListener> sConnectionListeners =
@@ -140,16 +140,16 @@ public class KeyguardTouchDelegate {
return false;
}
- public boolean isShowingAndNotHidden() {
+ public boolean isShowingAndNotOccluded() {
final IKeyguardService service = mService;
if (service != null) {
try {
- return service.isShowingAndNotHidden();
+ return service.isShowingAndNotOccluded();
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
} else {
- Slog.w(TAG, "isShowingAndNotHidden(): NO SERVICE!");
+ Slog.w(TAG, "isShowingAndNotOccluded(): NO SERVICE!");
}
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
index a74230b..d26b32f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarTransitions.java
@@ -117,7 +117,7 @@ public final class NavigationBarTransitions extends BarTransitions {
@Override
public void setContentVisible(boolean visible) {
final float alpha = visible ? 1 : 0;
- fadeContent(mView.getCameraButton(), alpha);
+ fadeContent(mView.getBackButton(), alpha);
fadeContent(mView.getSearchLight(), alpha);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index d9e0903..db26a42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -34,17 +34,23 @@ import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
+import android.os.PowerManager;
import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.MediaStore;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Display;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -92,12 +98,16 @@ public class NavigationBarView extends LinearLayout {
final static boolean WORKAROUND_INVALID_LAYOUT = true;
final static int MSG_CHECK_INVALID_LAYOUT = 8686;
+ private final float mCameraDragDistance;
+
// used to disable the camera icon in navbar when disabled by DPM
private boolean mCameraDisabledByDpm;
// performs manual animation in sync with layout transitions
private final NavTransitionListener mTransitionListener = new NavTransitionListener();
+ private final PowerManager mPowerManager;
+
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
private boolean mHomeAppearing;
@@ -155,22 +165,73 @@ public class NavigationBarView extends LinearLayout {
}
};
+ private final int mScaledTouchSlop;
+
private final OnTouchListener mCameraTouchListener = new OnTouchListener() {
+ private float mStartX;
+ private boolean mTouchSlopReached;
+ private boolean mSkipCancelAnimation;
+
@Override
- public boolean onTouch(View cameraButtonView, MotionEvent event) {
+ public boolean onTouch(final View cameraButtonView, MotionEvent event) {
+ float realX = event.getRawX();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// disable search gesture while interacting with camera
mDelegateHelper.setDisabled(true);
mBarTransitions.setContentVisible(false);
+ mStartX = realX;
+ mTouchSlopReached = false;
+ mSkipCancelAnimation = false;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ if (realX > mStartX) {
+ realX = mStartX;
+ }
+ if (realX < mStartX - mCameraDragDistance) {
+ ((KeyButtonView) cameraButtonView).setPressed(true);
+ mPowerManager.userActivity(event.getEventTime(), false);
+ } else {
+ ((KeyButtonView) cameraButtonView).setPressed(false);
+ }
+ if (realX < mStartX - mScaledTouchSlop) {
+ mTouchSlopReached = true;
+ }
+ cameraButtonView.setTranslationX(Math.max(realX - mStartX,
+ -mCameraDragDistance));
break;
case MotionEvent.ACTION_UP:
+ if (realX < mStartX - mCameraDragDistance) {
+ mContext.startActivityAsUser(
+ new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE),
+ UserHandle.CURRENT);
+ }
+ if (realX < mStartX - mScaledTouchSlop) {
+ mTouchSlopReached = true;
+ }
+ if (!mTouchSlopReached) {
+ mSkipCancelAnimation = true;
+ cameraButtonView.animate().translationX(-mCameraDragDistance / 2).
+ setInterpolator(new DecelerateInterpolator()).withEndAction(
+ new Runnable() {
+ @Override
+ public void run() {
+ cameraButtonView.animate().translationX(0).
+ setInterpolator(new AccelerateInterpolator());
+ }
+ });
+ }
case MotionEvent.ACTION_CANCEL:
+ ((KeyButtonView) cameraButtonView).setPressed(false);
mDelegateHelper.setDisabled(false);
mBarTransitions.setContentVisible(true);
+ if (!mSkipCancelAnimation) {
+ cameraButtonView.animate().translationX(0)
+ .setInterpolator(new AccelerateInterpolator(2f));
+ }
break;
}
- return KeyguardTouchDelegate.getInstance(getContext()).dispatch(event);
+ return true;
}
};
@@ -235,6 +296,9 @@ public class NavigationBarView extends LinearLayout {
KeyguardTouchDelegate.addListener(mKeyguardConnectionListener);
mCameraDisabledByDpm = isCameraDisabledByDpm();
watchForDevicePolicyChanges();
+ mCameraDragDistance = res.getDimension(R.dimen.camera_drag_distance);
+ mScaledTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
}
private void watchForDevicePolicyChanges() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index a3e35d1..324d6f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -151,7 +151,8 @@ public class PanelBar extends FrameLayout {
if (DEBUG) LOG("panelExpansionChanged: start state=%d panel=%s", mState, panel.getName());
mPanelExpandedFractionSum = 0f;
for (PanelView pv : mPanels) {
- final boolean visible = pv.getVisibility() == View.VISIBLE;
+ boolean visible = pv.getExpandedHeight() > 0;
+ pv.setVisibility(visible ? View.VISIBLE : View.GONE);
// adjust any other panels that may be partially visible
if (pv.getExpandedHeight() > 0f) {
if (mState == STATE_CLOSED) {
@@ -166,11 +167,6 @@ public class PanelBar extends FrameLayout {
if (thisFrac == 1f) fullyOpenedPanel = panel;
}
}
- if (pv.getExpandedHeight() > 0f) {
- if (!visible) pv.setVisibility(View.VISIBLE);
- } else {
- if (visible) pv.setVisibility(View.GONE);
- }
}
mPanelExpandedFractionSum /= mPanels.size();
if (fullyOpenedPanel != null && !mTracking) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6922ca5..10a9b64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -651,6 +651,12 @@ public class PanelView extends FrameLayout {
int newHeight = getMeasuredHeight();
if (newHeight != mMaxPanelHeight) {
mMaxPanelHeight = newHeight;
+ // If the user isn't actively poking us, let's rubberband to the content
+ if (!mTracking && !mRubberbanding && !mTimeAnimator.isStarted()
+ && mExpandedHeight > 0 && mExpandedHeight != mMaxPanelHeight
+ && mMaxPanelHeight > 0) {
+ mExpandedHeight = mMaxPanelHeight;
+ }
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(
getDesiredMeasureHeight(), MeasureSpec.AT_MOST);
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 841f3ca..ec9f3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -16,14 +16,16 @@
package com.android.systemui.statusbar.phone;
+
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.windowStateToString;
+import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT;
-import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
+import static com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -66,6 +68,7 @@ import android.util.EventLog;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -84,15 +87,19 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.DemoMode;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
+import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.GestureRecorder;
import com.android.systemui.statusbar.InterceptedNotifications;
+import com.android.systemui.statusbar.LatestItemView;
import com.android.systemui.statusbar.NotificationData;
import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationOverflowIconsView;
import com.android.systemui.statusbar.SignalClusterView;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -174,6 +181,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
StatusBarWindowView mStatusBarWindow;
PhoneStatusBarView mStatusBarView;
private int mStatusBarWindowState = WINDOW_STATE_SHOWING;
+ private StatusBarWindowManager mStatusBarWindowManager;
int mPixelFormat;
Object mQueueLock = new Object();
@@ -212,9 +220,12 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
// top bar
View mNotificationPanelHeader;
+ View mKeyguardStatusView;
+ int mKeyguardMaxNotificationCount;
View mDateTimeView;
View mClearButton;
ImageView mSettingsButton, mNotificationButton;
+ View mKeyguardSettingsFlipButton;
// carrier/wifi label
private TextView mCarrierLabel;
@@ -337,6 +348,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
private int mNavigationBarMode;
private Boolean mScreenOn;
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private ViewMediatorCallback mKeyguardViewMediatorCallback;
+
private final Runnable mAutohide = new Runnable() {
@Override
public void run() {
@@ -349,6 +363,14 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
private Runnable mOnFlipRunnable;
private InterceptedNotifications mIntercepted;
+ private final OnChildLocationsChangedListener mOnChildLocationsChangedListener =
+ new OnChildLocationsChangedListener() {
+ @Override
+ public void onChildLocationsChanged(NotificationStackScrollLayout stackScrollLayout) {
+ userActivity();
+ }
+ };
+
public void setOnFlipRunnable(Runnable onFlipRunnable) {
mOnFlipRunnable = onFlipRunnable;
}
@@ -388,6 +410,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
Settings.Global.getUriFor(SETTING_HEADS_UP_TICKER), true,
mHeadsUpObserver);
}
+ startKeyguard();
}
// ================================================================================
@@ -494,10 +517,21 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
mStackScroller = (NotificationStackScrollLayout) mStatusBarWindow.findViewById(
R.id.notification_stack_scroller);
mStackScroller.setLongPressListener(getNotificationLongClicker());
+ mStackScroller.setChildLocationsChangedListener(mOnChildLocationsChangedListener);
+
+ mKeyguardIconOverflowContainer = LayoutInflater.from(mContext).inflate(
+ R.layout.status_bar_notification_keyguard_overflow, mStackScroller, false);
+ ((LatestItemView) mKeyguardIconOverflowContainer.findViewById(R.id.container)).setLocked(true);
+ mOverflowIconsView = (NotificationOverflowIconsView) mKeyguardIconOverflowContainer.findViewById(
+ R.id.overflow_icons_view);
+ mOverflowIconsView.setMoreText(
+ (TextView) mKeyguardIconOverflowContainer.findViewById(R.id.more_text));
+ mStackScroller.addView(mKeyguardIconOverflowContainer);
mExpandedContents = mStackScroller;
mNotificationPanelHeader = mStatusBarWindow.findViewById(R.id.header);
+ mKeyguardStatusView = mStatusBarWindow.findViewById(R.id.keyguard_status_view);
mClearButton = mStatusBarWindow.findViewById(R.id.clear_all_button);
mClearButton.setOnClickListener(mClearButtonListener);
@@ -687,6 +721,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
return mStatusBarView;
}
+ private void startKeyguard() {
+ KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
+ mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
+ mStatusBarWindow, mStatusBarWindowManager);
+ mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
+ }
+
@Override
protected void onShowSearchPanel() {
if (mNavigationBarView != null) {
@@ -764,10 +805,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
}
- protected int getStatusBarGravity() {
- return Gravity.TOP | Gravity.FILL_HORIZONTAL;
- }
-
public int getStatusBarHeight() {
if (mNaturalBarHeight < 0) {
final Resources res = mContext.getResources();
@@ -1024,7 +1061,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
if (CLOSE_PANEL_WHEN_EMPTIED && mNotificationData.size() == 0
- && !mNotificationPanel.isTracking()) {
+ && !mNotificationPanel.isTracking() && !mOnKeyguard) {
animateCollapsePanels();
}
}
@@ -1093,7 +1130,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
ArrayList<View> toRemove = new ArrayList<View>();
for (int i=0; i< mStackScroller.getChildCount(); i++) {
View child = mStackScroller.getChildAt(i);
- if (!toShow.contains(child)) {
+ if (!toShow.contains(child) && child != mKeyguardIconOverflowContainer) {
toRemove.add(child);
}
}
@@ -1493,26 +1530,13 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
// Expand the window to encompass the full screen in anticipation of the drag.
// This is only possible to do atomically because the status bar is at the top of the screen!
- WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
- lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- lp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- lp.height = ViewGroup.LayoutParams.MATCH_PARENT;
- mWindowManager.updateViewLayout(mStatusBarWindow, lp);
+ mStatusBarWindowManager.setStatusBarExpanded(true);
visibilityChanged(true);
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
}
- private void releaseFocus() {
- if (mStatusBarWindow == null) return;
- WindowManager.LayoutParams lp =
- (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
- lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mWindowManager.updateViewLayout(mStatusBarWindow, lp);
- }
-
public void animateCollapsePanels() {
animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE);
}
@@ -1524,9 +1548,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
+ " flags=" + flags);
}
- // release focus immediately to kick off focus change transition
- releaseFocus();
-
if ((flags & CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL) == 0) {
mHandler.removeMessages(MSG_CLOSE_RECENTS_PANEL);
mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL);
@@ -1538,6 +1559,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
if (mStatusBarWindow != null) {
+
+ // release focus immediately to kick off focus change transition
+ mStatusBarWindowManager.setStatusBarFocusable(false);
+
mStatusBarWindow.cancelExpandHelper();
mStatusBarView.collapseAllPanels(true);
}
@@ -1796,11 +1821,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
visibilityChanged(false);
// Shrink the window to the size of the status bar only
- WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mStatusBarWindow.getLayoutParams();
- lp.height = getStatusBarHeight();
- lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- lp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
- mWindowManager.updateViewLayout(mStatusBarWindow, lp);
+ mStatusBarWindowManager.setStatusBarExpanded(false);
if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
@@ -1815,6 +1836,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
+
+ showBouncer();
}
public boolean interceptTouchEvent(MotionEvent event) {
@@ -2303,30 +2326,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
private void addStatusBarWindow() {
- // Put up the view
- final int height = getStatusBarHeight();
-
- // Now that the status bar window encompasses the sliding panel and its
- // translucent backdrop, the entire thing is made TRANSLUCENT and is
- // hardware-accelerated.
- final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- height,
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
- PixelFormat.TRANSLUCENT);
-
- lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
-
- lp.gravity = getStatusBarGravity();
- lp.setTitle("StatusBar");
- lp.packageName = mContext.getPackageName();
-
makeStatusBarView();
- mWindowManager.addView(mStatusBarWindow, lp);
+ mStatusBarWindowManager = new StatusBarWindowManager(mContext);
+ mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
}
void setNotificationIconVisibility(boolean visible, int anim) {
@@ -2464,8 +2466,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOn = false;
- // no waiting!
- makeExpandedInvisible();
notifyNavigationBarScreenOn(false);
notifyHeadsUpScreenOn(false);
finishBarAnimations();
@@ -2644,6 +2644,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_row_min_height);
mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_row_max_height);
+ mKeyguardMaxNotificationCount = res.getInteger(R.integer.keyguard_max_notification_count);
+
if (false) Log.v(TAG, "updateResources");
}
@@ -2815,4 +2817,120 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
((DemoMode)v).dispatchDemoCommand(command, args);
}
}
+
+ public boolean isOnKeyguard() {
+ return mOnKeyguard;
+ }
+
+ public void showKeyguard() {
+ mOnKeyguard = true;
+ instantExpandNotificationsPanel();
+ if (isFlippedToSettings()) {
+ flipToNotifications();
+ }
+ mStatusBarWindow.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
+ mKeyguardStatusView.setVisibility(View.VISIBLE);
+ mNotificationPanelHeader.setVisibility(View.GONE);
+ if (mKeyguardSettingsFlipButton == null) {
+ ViewStub flipStub = (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_flip_stub);
+ mKeyguardSettingsFlipButton = flipStub.inflate();
+ installSettingsButton(mKeyguardSettingsFlipButton);
+ }
+ mKeyguardSettingsFlipButton.setVisibility(View.VISIBLE);
+ mKeyguardSettingsFlipButton.findViewById(R.id.settings_button).setVisibility(View.VISIBLE);
+ mKeyguardSettingsFlipButton.findViewById(R.id.notification_button)
+ .setVisibility(View.INVISIBLE);
+ updateRowStates();
+ }
+
+ public void hideKeyguard() {
+ mOnKeyguard = false;
+ mStatusBarWindow.setSystemUiVisibility(0);
+ mKeyguardStatusView.setVisibility(View.GONE);
+ mNotificationPanelHeader.setVisibility(View.VISIBLE);
+ mKeyguardSettingsFlipButton.setVisibility(View.GONE);
+ updateRowStates();
+ }
+
+ public void userActivity() {
+ if (mOnKeyguard) {
+ mKeyguardViewMediatorCallback.userActivity();
+ }
+ }
+
+ public boolean onBackPressed() {
+ if (mOnKeyguard) {
+ return mStatusBarKeyguardViewManager.onBackPressed();
+ } else {
+ animateCollapsePanels();
+ return true;
+ }
+ }
+
+ private void showBouncer() {
+ if (mOnKeyguard) {
+ mStatusBarKeyguardViewManager.dismiss();
+ }
+ }
+
+ private void instantExpandNotificationsPanel() {
+ mExpandedVisible = true;
+ mNotificationPanel.setExpandedFraction(1);
+ }
+
+ @Override
+ protected int getMaxKeyguardNotifications() {
+ return mKeyguardMaxNotificationCount;
+ }
+
+ /**
+ * @return a ViewGroup that spans the entire panel which contains the quick settings
+ */
+ public ViewGroup getQuickSettingsOverlayParent() {
+ if (mHasSettingsPanel) {
+ if (mHasFlipSettings) {
+ return mNotificationPanel;
+ } else {
+ return mSettingsPanel;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private void installSettingsButton(View parent) {
+ final ImageView settingsButton =
+ (ImageView) mStatusBarWindow.findViewById(R.id.settings_button);
+ final ImageView notificationButton =
+ (ImageView) mStatusBarWindow.findViewById(R.id.notification_button);
+ if (settingsButton != null) {
+ settingsButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ animateExpandSettingsPanel();
+ v.setVisibility(View.INVISIBLE);
+ notificationButton.setVisibility(View.VISIBLE);
+ }
+ });
+ settingsButton.setVisibility(View.VISIBLE);
+ if (mHasSettingsPanel) {
+ // the settings panel is hiding behind this button
+ settingsButton.setImageResource(R.drawable.ic_notify_quicksettings);
+ } else {
+ // no settings panel, go straight to settings
+ settingsButton.setImageResource(R.drawable.ic_notify_settings);
+ }
+ }
+ if (notificationButton != null) {
+ notificationButton.setVisibility(View.INVISIBLE);
+ notificationButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ flipToNotifications();
+ v.setVisibility(View.INVISIBLE);
+ settingsButton.setVisibility(View.VISIBLE);
+ }
+ });
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index d9e3fdf..583fc3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -37,6 +37,7 @@ public class PhoneStatusBarView extends PanelBar {
PhoneStatusBar mBar;
int mScrimColor;
+ int mScrimColorKeyguard;
float mSettingsPanelDragzoneFrac;
float mSettingsPanelDragzoneMin;
@@ -52,6 +53,7 @@ public class PhoneStatusBarView extends PanelBar {
Resources res = getContext().getResources();
mScrimColor = res.getColor(R.color.notification_panel_scrim_color);
+ mScrimColorKeyguard = res.getColor(R.color.notification_panel_scrim_color_keyguard);
mSettingsPanelDragzoneMin = res.getDimension(R.dimen.settings_panel_dragzone_min);
try {
mSettingsPanelDragzoneFrac = res.getFraction(R.dimen.settings_panel_dragzone_fraction, 1, 1);
@@ -217,6 +219,7 @@ public class PhoneStatusBarView extends PanelBar {
if (panel == mFadingPanel && mScrimColor != 0 && ActivityManager.isHighEndGfx()
&& mBar.mStatusBarWindow != null) {
if (mShouldFade) {
+ int scrimColor = mBar.isOnKeyguard() ? mScrimColorKeyguard : mScrimColor;
frac = mPanelExpandedFractionSum; // don't judge me
// let's start this 20% of the way down the screen
frac = frac * 1.2f - 0.2f;
@@ -226,7 +229,7 @@ public class PhoneStatusBarView extends PanelBar {
// woo, special effects
final float k = (float)(1f-0.5f*(1f-Math.cos(3.14159f * Math.pow(1f-frac, 2f))));
// attenuate background color alpha by k
- final int color = (int) ((mScrimColor >>> 24) * k) << 24 | (mScrimColor & 0xFFFFFF);
+ final int color = (int) ((scrimColor >>> 24) * k) << 24 | (scrimColor & 0xFFFFFF);
mBar.mStatusBarWindow.setBackgroundColor(color);
}
}
@@ -249,5 +252,6 @@ public class PhoneStatusBarView extends PanelBar {
mBar.animateHeadsUp(mNotificationPanel == panel, mPanelExpandedFractionSum);
mBar.updateCarrierLabelVisibility(false);
+ mBar.userActivity();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index d67f7cd..b3fba76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -71,6 +71,7 @@ import android.widget.TextView;
import com.android.internal.app.MediaRouteDialogPresenter;
import com.android.systemui.R;
+import com.android.systemui.settings.UserSwitcherHostView;
import com.android.systemui.statusbar.phone.QuickSettingsModel.ActivityState;
import com.android.systemui.statusbar.phone.QuickSettingsModel.BluetoothState;
import com.android.systemui.statusbar.phone.QuickSettingsModel.RSSIState;
@@ -310,30 +311,28 @@ class QuickSettings {
collapsePanels();
}
- private void addUserTiles(ViewGroup parent, LayoutInflater inflater) {
+ private void addUserTiles(final ViewGroup parent, final LayoutInflater inflater) {
QuickSettingsTileView userTile = (QuickSettingsTileView)
inflater.inflate(R.layout.quick_settings_tile, parent, false);
userTile.setContent(R.layout.quick_settings_tile_user, inflater);
userTile.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- collapsePanels();
final UserManager um = UserManager.get(mContext);
if (um.isUserSwitcherEnabled()) {
- // Since keyguard and systemui were merged into the same process to save
- // memory, they share the same Looper and graphics context. As a result,
- // there's no way to allow concurrent animation while keyguard inflates.
- // The workaround is to add a slight delay to allow the animation to finish.
- mHandler.postDelayed(new Runnable() {
+ final ViewGroup switcherParent = getService().getQuickSettingsOverlayParent();
+ final UserSwitcherHostView switcher = (UserSwitcherHostView) inflater.inflate(
+ R.layout.user_switcher_host, switcherParent, false);
+ switcher.setFinishRunnable(new Runnable() {
+ @Override
public void run() {
- try {
- WindowManagerGlobal.getWindowManagerService().lockNow(null);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't show user switcher", e);
- }
+ switcherParent.removeView(switcher);
}
- }, 400); // TODO: ideally this would be tied to the collapse of the panel
+ });
+ switcher.refreshUsers();
+ switcherParent.addView(switcher);
} else {
+ collapsePanels();
Intent intent = ContactsContract.QuickContact.composeQuickContactsIntent(
mContext, v, ContactsContract.Profile.CONTENT_URI,
ContactsContract.QuickContact.MODE_LARGE, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
new file mode 100644
index 0000000..b8592c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -0,0 +1,194 @@
+/*
+ * 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.phone;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import com.android.internal.policy.IKeyguardShowCallback;
+import com.android.internal.widget.LockPatternUtils;
+import com.android.keyguard.KeyguardHostView;
+import com.android.keyguard.KeyguardSimpleHostView;
+import com.android.keyguard.R;
+import com.android.keyguard.ViewMediatorCallback;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+
+/**
+ * Manages creating, showing, hiding and resetting the keyguard within the status bar. Calls back
+ * via {@link ViewMediatorCallback} to poke the wake lock and report that the keyguard is done,
+ * which is in turn, reported to this class by the current
+ * {@link com.android.keyguard.KeyguardViewBase}.
+ */
+public class StatusBarKeyguardViewManager {
+ private static String TAG = "StatusBarKeyguardViewManager";
+
+ private final Context mContext;
+
+ private LockPatternUtils mLockPatternUtils;
+ private ViewMediatorCallback mViewMediatorCallback;
+ private PhoneStatusBar mPhoneStatusBar;
+
+ private ViewGroup mContainer;
+ private StatusBarWindowManager mStatusBarWindowManager;
+
+ private boolean mScreenOn = false;
+ private KeyguardBouncer mBouncer;
+ private boolean mShowing;
+
+ public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
+ LockPatternUtils lockPatternUtils) {
+ mContext = context;
+ mViewMediatorCallback = callback;
+ mLockPatternUtils = lockPatternUtils;
+ }
+
+ public void registerStatusBar(PhoneStatusBar phoneStatusBar,
+ ViewGroup container, StatusBarWindowManager statusBarWindowManager) {
+ mPhoneStatusBar = phoneStatusBar;
+ mContainer = container;
+ mStatusBarWindowManager = statusBarWindowManager;
+ mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
+ mStatusBarWindowManager, container);
+ }
+
+ /**
+ * Show the keyguard. Will handle creating and attaching to the view manager
+ * lazily.
+ */
+ public void show(Bundle options) {
+ mShowing = true;
+ mStatusBarWindowManager.setKeyguardShowing(true);
+ mPhoneStatusBar.showKeyguard();
+ mBouncer.prepare();
+ updateBackButtonState();
+ }
+
+ public void showBouncer() {
+ mBouncer.show();
+ updateBackButtonState();
+ }
+
+ /**
+ * Reset the state of the view.
+ */
+ public void reset() {
+ mBouncer.reset();
+ mPhoneStatusBar.showKeyguard();
+ updateBackButtonState();
+ }
+
+ public void onScreenTurnedOff() {
+ mScreenOn = false;
+ mBouncer.onScreenTurnedOff();
+ }
+
+ public void onScreenTurnedOn(final IKeyguardShowCallback callback) {
+ mScreenOn = true;
+ mBouncer.onScreenTurnedOn();
+ if (callback != null) {
+ callbackAfterDraw(callback);
+ }
+ }
+
+ private void callbackAfterDraw(final IKeyguardShowCallback callback) {
+ mContainer.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callback.onShown(mContainer.getWindowToken());
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Exception calling onShown():", e);
+ }
+ }
+ });
+ }
+
+ public void verifyUnlock() {
+ dismiss();
+ }
+
+ public void setNeedsInput(boolean needsInput) {
+ mStatusBarWindowManager.setKeyguardNeedsInput(needsInput);
+ }
+
+ public void updateUserActivityTimeout() {
+ mStatusBarWindowManager.setKeyguardUserActivityTimeout(mBouncer.getUserActivityTimeout());
+ }
+
+ public void setOccluded(boolean occluded) {
+ mStatusBarWindowManager.setKeyguardOccluded(occluded);
+ }
+
+ /**
+ * Hides the keyguard view
+ */
+ public void hide() {
+ mShowing = false;
+ mPhoneStatusBar.hideKeyguard();
+ mStatusBarWindowManager.setKeyguardShowing(false);
+ mBouncer.hide();
+ mViewMediatorCallback.keyguardGone();
+ }
+
+ /**
+ * Dismisses the keyguard by going to the next screen or making it gone.
+ */
+ public void dismiss() {
+ if (mScreenOn) {
+ showBouncer();
+ }
+ }
+
+ /**
+ * @return Whether the keyguard is showing
+ */
+ public boolean isShowing() {
+ return mShowing;
+ }
+
+ /**
+ * Notifies this manager that the back button has been pressed.
+ *
+ * @return whether the back press has been handled
+ */
+ public boolean onBackPressed() {
+ if (mBouncer.isShowing()) {
+ mBouncer.hide();
+ mPhoneStatusBar.showKeyguard();
+ updateBackButtonState();
+ return true;
+ }
+ return false;
+ }
+
+ private void updateBackButtonState() {
+ int vis = mContainer.getSystemUiVisibility();
+ if (mBouncer.isShowing()) {
+ mContainer.setSystemUiVisibility(vis & ~View.STATUS_BAR_DISABLE_BACK);
+ } else {
+ mContainer.setSystemUiVisibility(vis | View.STATUS_BAR_DISABLE_BACK);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
new file mode 100644
index 0000000..6153cde
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -0,0 +1,200 @@
+/*
+ * 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.phone;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.SystemProperties;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.keyguard.R;
+
+/**
+ * Encapsulates all logic for the status bar window state management.
+ */
+public class StatusBarWindowManager {
+
+ private final Context mContext;
+ private final WindowManager mWindowManager;
+ private View mStatusBarView;
+ private WindowManager.LayoutParams mLp;
+ private int mBarHeight;
+ private final boolean mKeyguardScreenRotation;
+
+ private final State mCurrentState = new State();
+
+ public StatusBarWindowManager(Context context) {
+ mContext = context;
+ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
+ }
+
+ private boolean shouldEnableKeyguardScreenRotation() {
+ Resources res = mContext.getResources();
+ return SystemProperties.getBoolean("lockscreen.rot_override", false)
+ || res.getBoolean(R.bool.config_enableLockScreenRotation);
+ }
+
+ /**
+ * Adds the status bar view to the window manager.
+ *
+ * @param statusBarView The view to add.
+ * @param barHeight The height of the status bar in collapsed state.
+ */
+ public void add(View statusBarView, int barHeight) {
+
+ // Now that the status bar window encompasses the sliding panel and its
+ // translucent backdrop, the entire thing is made TRANSLUCENT and is
+ // hardware-accelerated.
+ mLp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ barHeight,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
+ PixelFormat.TRANSLUCENT);
+
+ mLp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
+ mLp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ mLp.setTitle("StatusBar");
+ mLp.packageName = mContext.getPackageName();
+ mStatusBarView = statusBarView;
+ mBarHeight = barHeight;
+ mWindowManager.addView(mStatusBarView, mLp);
+ }
+
+ private void applyKeyguardFlags(State state) {
+ if (state.keyguardShowing) {
+ mLp.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+ mLp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+ } else {
+ mLp.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+ mLp.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+ }
+ }
+
+ private void adjustScreenOrientation(State state) {
+ if (!state.isKeyguardShowingAndNotOccluded() || mKeyguardScreenRotation) {
+ mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_USER;
+ } else {
+ mLp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+ }
+ }
+
+ private void applyFocusableFlag(State state) {
+ if (state.isKeyguardShowingAndNotOccluded() && state.keyguardNeedsInput) {
+ mLp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mLp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ } else if (state.isKeyguardShowingAndNotOccluded() || state.statusBarFocusable) {
+ mLp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mLp.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ } else {
+ mLp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mLp.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ }
+ }
+
+ private void applyHeight(State state) {
+ boolean expanded = state.isKeyguardShowingAndNotOccluded() || state.statusBarExpanded;
+ if (expanded) {
+ mLp.height = ViewGroup.LayoutParams.MATCH_PARENT;
+ } else {
+ mLp.height = mBarHeight;
+ }
+ }
+
+ private void applyUserActivityTimeout(State state) {
+ if (state.isKeyguardShowingAndNotOccluded()) {
+ mLp.userActivityTimeout = state.keyguardUserActivityTimeout;
+ } else {
+ mLp.userActivityTimeout = -1;
+ }
+ }
+
+ private void applyInputFeatures(State state) {
+ if (state.isKeyguardShowingAndNotOccluded()) {
+ mLp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+ } else {
+ mLp.inputFeatures &= ~WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
+ }
+ }
+
+ private void apply(State state) {
+ applyKeyguardFlags(state);
+ applyFocusableFlag(state);
+ adjustScreenOrientation(state);
+ applyHeight(state);
+ applyUserActivityTimeout(state);
+ applyInputFeatures(state);
+ mWindowManager.updateViewLayout(mStatusBarView, mLp);
+ }
+
+ public void setKeyguardShowing(boolean showing) {
+ mCurrentState.keyguardShowing = showing;
+ apply(mCurrentState);
+ }
+
+ public void setKeyguardOccluded(boolean occluded) {
+ mCurrentState.keyguardOccluded = occluded;
+ apply(mCurrentState);
+ }
+
+ public void setKeyguardNeedsInput(boolean needsInput) {
+ mCurrentState.keyguardNeedsInput = needsInput;
+ apply(mCurrentState);
+ }
+
+ public void setStatusBarExpanded(boolean expanded) {
+ mCurrentState.statusBarExpanded = expanded;
+ mCurrentState.statusBarFocusable = expanded;
+ apply(mCurrentState);
+ }
+
+ public void setStatusBarFocusable(boolean focusable) {
+ mCurrentState.statusBarFocusable = focusable;
+ apply(mCurrentState);
+ }
+
+ public void setKeyguardUserActivityTimeout(long timeout) {
+ mCurrentState.keyguardUserActivityTimeout = timeout;
+ apply(mCurrentState);
+ }
+
+ private static class State {
+ boolean keyguardShowing;
+ boolean keyguardOccluded;
+ boolean keyguardNeedsInput;
+ boolean statusBarExpanded;
+ boolean statusBarFocusable;
+ long keyguardUserActivityTimeout;
+
+ private boolean isKeyguardShowingAndNotOccluded() {
+ return keyguardShowing && !keyguardOccluded;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index a7121c4..dd89f47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -79,7 +79,7 @@ public class StatusBarWindowView extends FrameLayout
switch (event.getKeyCode()) {
case KeyEvent.KEYCODE_BACK:
if (!down) {
- mService.animateCollapsePanels();
+ mService.onBackPressed();
}
return true;
}
@@ -90,7 +90,8 @@ public class StatusBarWindowView extends FrameLayout
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean intercept = false;
if (mNotificationPanel.isFullyExpanded()
- && mStackScrollLayout.getVisibility() == View.VISIBLE) {
+ && mStackScrollLayout.getVisibility() == View.VISIBLE
+ && !mService.isOnKeyguard()) {
intercept = mExpandHelper.onInterceptTouchEvent(ev);
}
if (!intercept) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index f31896a..9800bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -656,9 +656,11 @@ public class NotificationStackScrollLayout extends ViewGroup
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
- height += child.getHeight();
- if (i < getChildCount()-1) {
- height += mPaddingBetweenElements;
+ if (child.getVisibility() != View.GONE) {
+ height += child.getHeight();
+ if (i < getChildCount()-1) {
+ height += mPaddingBetweenElements;
+ }
}
}
mContentHeight = height;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 5506a55..431f6fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -23,6 +23,8 @@ import android.view.ViewGroup;
import com.android.systemui.R;
import com.android.systemui.statusbar.ExpandableNotificationRow;
+import java.util.ArrayList;
+
/**
* The Algorithm of the {@link com.android.systemui.statusbar.stack
* .NotificationStackScrollLayout} which can be queried for {@link com.android.systemui.statusbar
@@ -98,6 +100,7 @@ public class StackScrollAlgorithm {
algorithmState.lastTopStackIndex = 0;
algorithmState.scrollY = resultState.getScrollY();
algorithmState.itemsInBottomStack = 0.0f;
+ updateVisibleChildren(resultState, algorithmState);
// Phase 1:
findNumberOfItemsInTopStackAndUpdateState(resultState, algorithmState);
@@ -113,6 +116,23 @@ public class StackScrollAlgorithm {
}
/**
+ * Update the visible children on the state.
+ */
+ private void updateVisibleChildren(StackScrollState resultState,
+ StackScrollAlgorithmState state) {
+ ViewGroup hostView = resultState.getHostView();
+ int childCount = hostView.getChildCount();
+ state.visibleChildren.clear();
+ state.visibleChildren.ensureCapacity(childCount);
+ for (int i = 0; i < childCount; i++) {
+ View v = hostView.getChildAt(i);
+ if (v.getVisibility() != View.GONE) {
+ state.visibleChildren.add(v);
+ }
+ }
+ }
+
+ /**
* Determine the positions for the views. This is the main part of the algorithm.
*
* @param resultState The result state to update if a change to the properties of a child occurs
@@ -135,11 +155,10 @@ public class StackScrollAlgorithm {
// How far in is the element currently transitioning into the bottom stack.
float yPositionInScrollView = 0.0f;
- ViewGroup hostView = resultState.getHostView();
- int childCount = hostView.getChildCount();
+ int childCount = algorithmState.visibleChildren.size();
int numberOfElementsCompletelyIn = (int) algorithmState.itemsInTopStack;
for (int i = 0; i < childCount; i++) {
- View child = hostView.getChildAt(i);
+ View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
childViewState.yTranslation = currentYPosition;
childViewState.location = StackScrollState.ViewState.LOCATION_UNKNOWN;
@@ -317,12 +336,11 @@ public class StackScrollAlgorithm {
// The y Position if the element would be in a regular scrollView
float yPositionInScrollView = 0.0f;
- ViewGroup hostView = resultState.getHostView();
- int childCount = hostView.getChildCount();
+ int childCount = algorithmState.visibleChildren.size();
// find the number of elements in the top stack.
for (int i = 0; i < childCount; i++) {
- View child = hostView.getChildAt(i);
+ View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
int childHeight = child.getHeight();
float yPositionInScrollViewAfterElement = yPositionInScrollView
@@ -397,10 +415,9 @@ public class StackScrollAlgorithm {
*/
private void updateZValuesForState(StackScrollState resultState,
StackScrollAlgorithmState algorithmState) {
- ViewGroup hostView = resultState.getHostView();
- int childCount = hostView.getChildCount();
+ int childCount = algorithmState.visibleChildren.size();
for (int i = 0; i < childCount; i++) {
- View child = hostView.getChildAt(i);
+ View child = algorithmState.visibleChildren.get(i);
StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
if (i < algorithmState.itemsInTopStack) {
float stackIndex = algorithmState.itemsInTopStack - i;
@@ -434,8 +451,8 @@ public class StackScrollAlgorithm {
}
private void updateFirstChildHeightWhileExpanding(ViewGroup hostView) {
- if (hostView.getChildCount() > 0) {
- mFirstChildWhileExpanding = hostView.getChildAt(0);
+ mFirstChildWhileExpanding = findFirstVisibleChild(hostView);
+ if (mFirstChildWhileExpanding != null) {
if (mExpandedOnStart) {
// We are collapsing the shade, so the first child can get as most as high as the
@@ -447,11 +464,21 @@ public class StackScrollAlgorithm {
mFirstChildMaxHeight = getMaxAllowedChildHeight(mFirstChildWhileExpanding);
}
} else {
- mFirstChildWhileExpanding = null;
mFirstChildMaxHeight = 0;
}
}
+ private View findFirstVisibleChild(ViewGroup container) {
+ int childCount = container.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = container.getChildAt(i);
+ if (child.getVisibility() != View.GONE) {
+ return child;
+ }
+ }
+ return null;
+ }
+
public void onExpansionStopped() {
mIsExpansionChanging = false;
mFirstChildWhileExpanding = null;
@@ -501,6 +528,11 @@ public class StackScrollAlgorithm {
* how far in is the element currently transitioning into the bottom stack
*/
public float partialInBottom;
+
+ /**
+ * The children from the host view which are not gone.
+ */
+ public final ArrayList<View> visibleChildren = new ArrayList<View>();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 67a1735..06a08f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -63,7 +63,8 @@ public class StackScrollState {
}
// initialize with the default values of the view
viewState.height = child.getHeight();
- viewState.alpha = 1.0f;
+ viewState.alpha = 1;
+ viewState.gone = child.getVisibility() == View.GONE;
}
}
@@ -116,7 +117,7 @@ public class StackScrollState {
// apply visibility
int oldVisibility = child.getVisibility();
int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
- if (newVisibility != oldVisibility) {
+ if (newVisibility != oldVisibility && !state.gone) {
child.setVisibility(newVisibility);
}
@@ -164,6 +165,7 @@ public class StackScrollState {
float yTranslation;
float zTranslation;
int height;
+ boolean gone;
/**
* The location this view is currently rendered at.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index dd13e31..d615542 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -22,6 +22,7 @@ import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
+import com.android.internal.policy.IKeyguardShowCallback;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.statusbar.BaseStatusBar;
@@ -93,10 +94,6 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
- protected void createAndAddWindows() {
- }
-
- @Override
protected WindowManager.LayoutParams getSearchLayoutParams(
LayoutParams layoutParams) {
return null;
@@ -141,10 +138,19 @@ public class TvStatusBar extends BaseStatusBar {
}
@Override
+ protected int getMaxKeyguardNotifications() {
+ return 0;
+ }
+
+ @Override
public void animateExpandSettingsPanel() {
}
@Override
+ protected void createAndAddWindows() {
+ }
+
+ @Override
protected void refreshLayout(int layoutDirection) {
}