Checkpoint status bar factoring. Now it builds and doesn't crash at boot.
Change-Id: I23f2045abfec0b414d5381f5e609b7267da7f21a
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 1e007d36..3434dca 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1567,23 +1567,6 @@
<!-- A format string for 12-hour time of day, just the hour, not the minute, with capital "AM" or "PM" (example: "3PM"). -->
<string name="hour_cap_ampm">"<xliff:g id="hour" example="3">%-l</xliff:g><xliff:g id="ampm" example="PM">%p</xliff:g>"</string>
- <!-- The text for the button in the notification window-shade that clears
- all of the currently visible notifications. -->
- <string name="status_bar_clear_all_button">Clear</string>
-
- <!-- The label in the bar at the top of the status bar when there are no notifications
- showing. -->
- <string name="status_bar_no_notifications_title">No notifications</string>
-
- <!-- The label for the group of notifications for ongoing events in the opened version of
- the status bar. An ongoing call is the prime example of this. The MP3 music player
- might be another example. -->
- <string name="status_bar_ongoing_events_title">Ongoing</string>
-
- <!-- The label for the group of notifications for recent events in the opened version of
- the status bar. Recently received text messsages (SMS), emails, calendar alerts, etc. -->
- <string name="status_bar_latest_events_title">Notifications</string>
-
<!-- The big percent text in the middle of the battery icon that appears when you plug in
the charger. -->
<string name="battery_status_text_percent_format"><xliff:g id="number" example="50">%d</xliff:g><xliff:g id="percent" example="%">%%</xliff:g></string>
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal.9.png
new file mode 100644
index 0000000..baafed6
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable.9.png
new file mode 100644
index 0000000..175197b
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
new file mode 100644
index 0000000..ec1feff
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_pressed.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_pressed.9.png
new file mode 100644
index 0000000..c1f9a0f
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_selected.9.png b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_selected.9.png
new file mode 100644
index 0000000..0ea3f40
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png b/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png
new file mode 100755
index 0000000..1c7f9db
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/status_bar_close_on.9.png b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_close_on.9.png
new file mode 100644
index 0000000..5acf638
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_close_on.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/status_bar_header_background.9.png b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_header_background.9.png
new file mode 100644
index 0000000..be36ff2
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_header_background.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png b/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png
new file mode 100644
index 0000000..dcca695
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png
new file mode 100644
index 0000000..70f7cc2
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png
new file mode 100644
index 0000000..e6dab63
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal.9.png
new file mode 100644
index 0000000..bcedd5f
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable.9.png
new file mode 100644
index 0000000..ac6260f
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
new file mode 100644
index 0000000..4ee1b3f
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_pressed.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_pressed.9.png
new file mode 100644
index 0000000..25e38f4
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_pressed.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_selected.9.png b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_selected.9.png
new file mode 100644
index 0000000..cc209c6
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_selected.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png b/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png
new file mode 100644
index 0000000..e129ba9
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/status_bar_close_on.9.png b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_close_on.9.png
new file mode 100644
index 0000000..9cbd9fe
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_close_on.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/status_bar_header_background.9.png b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_header_background.9.png
new file mode 100644
index 0000000..fa9a90c
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_header_background.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png b/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png
new file mode 100644
index 0000000..eb7c1a4
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png
new file mode 100644
index 0000000..13b18d8
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png
new file mode 100644
index 0000000..dbcefee
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png
Binary files differ
diff --git a/packages/StatusBarPhone/res/drawable/btn_default_small.xml b/packages/StatusBarPhone/res/drawable/btn_default_small.xml
new file mode 100644
index 0000000..5485ea0
--- /dev/null
+++ b/packages/StatusBarPhone/res/drawable/btn_default_small.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_window_focused="false" android:state_enabled="true"
+ android:drawable="@drawable/btn_default_small_normal" />
+ <item android:state_window_focused="false" android:state_enabled="false"
+ android:drawable="@drawable/btn_default_small_normal_disable" />
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_default_small_pressed" />
+ <item android:state_focused="true" android:state_enabled="true"
+ android:drawable="@drawable/btn_default_small_selected" />
+ <item android:state_enabled="true"
+ android:drawable="@drawable/btn_default_small_normal" />
+ <item android:state_focused="true"
+ android:drawable="@drawable/btn_default_small_normal_disable_focused" />
+ <item
+ android:drawable="@drawable/btn_default_small_normal_disable" />
+</selector>
+
diff --git a/core/res/res/layout/status_bar.xml b/packages/StatusBarPhone/res/layout/status_bar.xml
similarity index 100%
rename from core/res/res/layout/status_bar.xml
rename to packages/StatusBarPhone/res/layout/status_bar.xml
diff --git a/packages/StatusBarPhone/res/layout/status_bar_expanded.xml b/packages/StatusBarPhone/res/layout/status_bar_expanded.xml
new file mode 100644
index 0000000..30138a7
--- /dev/null
+++ b/packages/StatusBarPhone/res/layout/status_bar_expanded.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* apps/common/assets/default/default/skins/StatusBar.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+
+<com.android.server.status.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants"
+ >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="3dp"
+ android:paddingBottom="5dp"
+ android:paddingRight="3dp"
+ android:background="@drawable/status_bar_header_background"
+ >
+ <LinearLayout
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginTop="1dp"
+ android:layout_marginLeft="5dp"
+ android:layout_gravity="center_vertical"
+ android:paddingBottom="1dp"
+ android:orientation="vertical"
+ >
+ <TextView android:id="@+id/plmnLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorSecondaryInverse"
+ android:paddingLeft="4dp"
+ />
+ <TextView android:id="@+id/spnLabel"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textColor="?android:attr/textColorSecondaryInverse"
+ android:paddingLeft="4dp"
+ />
+ </LinearLayout>
+ <TextView android:id="@+id/clear_all_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginTop="4dp"
+ android:layout_marginBottom="1dp"
+ android:textSize="14sp"
+ android:textColor="#ff000000"
+ android:text="@string/status_bar_clear_all_button"
+ style="?android:attr/buttonStyle"
+ android:paddingLeft="15dp"
+ android:paddingRight="15dp"
+ android:background="@drawable/btn_default_small"
+ />
+ </LinearLayout>
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ >
+ <ScrollView
+ android:id="@+id/scroll"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fadingEdge="none"
+ >
+ <com.android.server.status.NotificationLinearLayout
+ android:id="@+id/notificationLinearLayout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+
+ <TextView android:id="@+id/noNotificationsTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/title_bar_portrait"
+ android:paddingLeft="5dp"
+ android:textAppearance="@style/TextAppearance.StatusBarTitle"
+ android:text="@string/status_bar_no_notifications_title"
+ />
+
+ <TextView android:id="@+id/ongoingTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/title_bar_portrait"
+ android:paddingLeft="5dp"
+ android:textAppearance="@style/TextAppearance.StatusBarTitle"
+ android:text="@string/status_bar_ongoing_events_title"
+ />
+ <LinearLayout android:id="@+id/ongoingItems"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ />
+
+ <TextView android:id="@+id/latestTitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/title_bar_portrait"
+ android:paddingLeft="5dp"
+ android:textAppearance="@style/TextAppearance.StatusBarTitle"
+ android:text="@string/status_bar_latest_events_title"
+ />
+ <LinearLayout android:id="@+id/latestItems"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ />
+ </com.android.server.status.NotificationLinearLayout>
+ </ScrollView>
+
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/title_bar_shadow"
+ android:scaleType="fitXY"
+ />
+
+ </FrameLayout>
+</com.android.server.status.ExpandedView>
diff --git a/packages/StatusBarPhone/res/layout/status_bar_tracking.xml b/packages/StatusBarPhone/res/layout/status_bar_tracking.xml
new file mode 100644
index 0000000..c0a7a97
--- /dev/null
+++ b/packages/StatusBarPhone/res/layout/status_bar_tracking.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2008 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.
+-->
+
+<com.android.server.status.TrackingView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:visibility="gone"
+ android:focusable="true"
+ android:descendantFocusability="afterDescendants"
+ android:paddingBottom="0px"
+ android:paddingLeft="0px"
+ android:paddingRight="0px"
+ >
+
+ <com.android.server.status.TrackingPatternView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ />
+
+ <com.android.server.status.CloseDragHandle android:id="@+id/close"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:scaleType="fitXY"
+ android:src="@drawable/status_bar_close_on"/>
+
+ </com.android.server.status.CloseDragHandle>
+
+</com.android.server.status.TrackingView>
diff --git a/packages/StatusBarPhone/res/values/arrays.xml b/packages/StatusBarPhone/res/values/arrays.xml
new file mode 100644
index 0000000..9076a47
--- /dev/null
+++ b/packages/StatusBarPhone/res/values/arrays.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/colors.xml
+**
+** Copyright 2006, 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+ <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the
+ icons in the status bar that are not notifications. -->
+ <string-array name="status_bar_icon_order">
+ <item><xliff:g id="id">clock</xliff:g></item>
+ <item><xliff:g id="id">secure</xliff:g></item>
+ <item><xliff:g id="id">alarm_clock</xliff:g></item>
+ <item><xliff:g id="id">battery</xliff:g></item>
+ <item><xliff:g id="id">phone_signal</xliff:g></item>
+ <item><xliff:g id="id">phone_evdo_signal</xliff:g></item>
+ <item><xliff:g id="id">data_connection</xliff:g></item>
+ <item><xliff:g id="id">cdma_eri</xliff:g></item>
+ <item><xliff:g id="id">tty</xliff:g></item>
+ <item><xliff:g id="id">volume</xliff:g></item>
+ <item><xliff:g id="id">mute</xliff:g></item>
+ <item><xliff:g id="id">speakerphone</xliff:g></item>
+ <item><xliff:g id="id">wifi</xliff:g></item>
+ <item><xliff:g id="id">tty</xliff:g></item>
+ <item><xliff:g id="id">bluetooth</xliff:g></item>
+ <item><xliff:g id="id">gps</xliff:g></item>
+ <item><xliff:g id="id">sync_active</xliff:g></item>
+ <item><xliff:g id="id">sync_failing</xliff:g></item>
+ <item><xliff:g id="id">ime</xliff:g></item>
+ </string-array>
+
+</resources>
diff --git a/packages/StatusBarPhone/res/values/dimens.xml b/packages/StatusBarPhone/res/values/dimens.xml
new file mode 100644
index 0000000..93cf377
--- /dev/null
+++ b/packages/StatusBarPhone/res/values/dimens.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2006, 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.
+*/
+-->
+<resources>
+ <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
+ <dimen name="status_bar_edge_ignore">5dp</dimen>
+</resources>
+
diff --git a/packages/StatusBarPhone/res/values/strings.xml b/packages/StatusBarPhone/res/values/strings.xml
index 40ac66a..8e42b59 100644
--- a/packages/StatusBarPhone/res/values/strings.xml
+++ b/packages/StatusBarPhone/res/values/strings.xml
@@ -19,4 +19,22 @@
<resources>
<!-- Name of the status bar as seen in the applications info settings page. -->
<string name="app_label">Status Bar</string>
+
+ <!-- The text for the button in the notification window-shade that clears
+ all of the currently visible notifications. -->
+ <string name="status_bar_clear_all_button">Clear</string>
+
+ <!-- The label in the bar at the top of the status bar when there are no notifications
+ showing. -->
+ <string name="status_bar_no_notifications_title">No notifications</string>
+
+ <!-- The label for the group of notifications for ongoing events in the opened version of
+ the status bar. An ongoing call is the prime example of this. The MP3 music player
+ might be another example. -->
+ <string name="status_bar_ongoing_events_title">Ongoing</string>
+
+ <!-- The label for the group of notifications for recent events in the opened version of
+ the status bar. Recently received text messsages (SMS), emails, calendar alerts, etc. -->
+ <string name="status_bar_latest_events_title">Notifications</string>
+
</resources>
diff --git a/packages/StatusBarPhone/res/values/styles.xml b/packages/StatusBarPhone/res/values/styles.xml
new file mode 100644
index 0000000..41c8ae7
--- /dev/null
+++ b/packages/StatusBarPhone/res/values/styles.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2006 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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="TextAppearance.StatusBarTitle" parent="@android:style/TextAppearance">
+ <item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
+ <item name="android:textStyle">bold</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+
+
+</resources>
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/AnimatedImageView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/AnimatedImageView.java
new file mode 100644
index 0000000..3411f29
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/AnimatedImageView.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+import android.widget.RemoteViews.RemoteView;
+
+@RemoteView
+public class AnimatedImageView extends ImageView {
+ AnimationDrawable mAnim;
+ boolean mAttached;
+
+ public AnimatedImageView(Context context) {
+ super(context);
+ }
+
+ public AnimatedImageView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ private void updateAnim() {
+ Drawable drawable = getDrawable();
+ if (mAttached && mAnim != null) {
+ mAnim.stop();
+ }
+ if (drawable instanceof AnimationDrawable) {
+ mAnim = (AnimationDrawable)drawable;
+ if (mAttached) {
+ mAnim.start();
+ }
+ } else {
+ mAnim = null;
+ }
+ }
+
+ @Override
+ public void setImageDrawable(Drawable drawable) {
+ super.setImageDrawable(drawable);
+ updateAnim();
+ }
+
+ @Override
+ @android.view.RemotableViewMethod
+ public void setImageResource(int resid) {
+ super.setImageResource(resid);
+ updateAnim();
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ if (mAnim != null) {
+ mAnim.start();
+ }
+ mAttached = true;
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mAnim != null) {
+ mAnim.stop();
+ }
+ mAttached = false;
+ }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/CloseDragHandle.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/CloseDragHandle.java
new file mode 100644
index 0000000..324c145
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/CloseDragHandle.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.widget.LinearLayout;
+
+
+public class CloseDragHandle extends LinearLayout {
+ PhoneStatusBarService mService;
+
+ public CloseDragHandle(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /**
+ * Ensure that, if there is no target under us to receive the touch,
+ * that we process it ourself. This makes sure that onInterceptTouchEvent()
+ * is always called for the entire gesture.
+ */
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() != MotionEvent.ACTION_DOWN) {
+ mService.interceptTouchEvent(event);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ return mService.interceptTouchEvent(event)
+ ? true : super.onInterceptTouchEvent(event);
+ }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/DateView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/DateView.java
new file mode 100644
index 0000000..e36b124
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/DateView.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.widget.TextView;
+import android.view.MotionEvent;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+public final class DateView extends TextView {
+ private static final String TAG = "DateView";
+
+ private boolean mUpdating = false;
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_TIME_TICK)
+ || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+ updateClock();
+ }
+ }
+ };
+
+ public DateView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ setUpdates(false);
+ }
+
+ @Override
+ protected int getSuggestedMinimumWidth() {
+ // makes the large background bitmap not force us to full width
+ return 0;
+ }
+
+ private final void updateClock() {
+ Date now = new Date();
+ setText(DateFormat.getDateInstance(DateFormat.LONG).format(now));
+ }
+
+ void setUpdates(boolean update) {
+ if (update != mUpdating) {
+ mUpdating = update;
+ if (update) {
+ // Register for Intent broadcasts for the clock and battery
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ mContext.registerReceiver(mIntentReceiver, filter, null, null);
+ updateClock();
+ } else {
+ mContext.unregisterReceiver(mIntentReceiver);
+ }
+ }
+ }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/ExpandedView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/ExpandedView.java
new file mode 100644
index 0000000..6b357e8
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/ExpandedView.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+import android.util.Slog;
+
+
+public class ExpandedView extends LinearLayout {
+ PhoneStatusBarService mService;
+ int mPrevHeight = -1;
+
+ public ExpandedView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ }
+
+ /** We want to shrink down to 0, and ignore the background. */
+ @Override
+ public int getSuggestedMinimumHeight() {
+ return 0;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ int height = bottom - top;
+ if (height != mPrevHeight) {
+ //Slog.d(PhoneStatusBarService.TAG, "height changed old=" + mPrevHeight
+ // + " new=" + height);
+ mPrevHeight = height;
+ mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
+ }
+ }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/FixedSizeDrawable.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/FixedSizeDrawable.java
new file mode 100644
index 0000000..3e40443
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/FixedSizeDrawable.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.graphics.drawable.Drawable;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.util.Slog;
+
+class FixedSizeDrawable extends Drawable {
+ Drawable mDrawable;
+ int mLeft;
+ int mTop;
+ int mRight;
+ int mBottom;
+
+ FixedSizeDrawable(Drawable that) {
+ mDrawable = that;
+ }
+
+ public void setFixedBounds(int l, int t, int r, int b) {
+ mLeft = l;
+ mTop = t;
+ mRight = r;
+ mBottom = b;
+ }
+
+ public void setBounds(Rect bounds) {
+ mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
+ }
+
+ public void setBounds(int l, int t, int r, int b) {
+ mDrawable.setBounds(mLeft, mTop, mRight, mBottom);
+ }
+
+ public void draw(Canvas canvas) {
+ mDrawable.draw(canvas);
+ }
+
+ public int getOpacity() {
+ return mDrawable.getOpacity();
+ }
+
+ public void setAlpha(int alpha) {
+ mDrawable.setAlpha(alpha);
+ }
+
+ public void setColorFilter(ColorFilter cf) {
+ mDrawable.setColorFilter(cf);
+ }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconData.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconData.java
new file mode 100644
index 0000000..354cf1c
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconData.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.util.Slog;
+
+public class IconData {
+ /**
+ * Indicates ths item represents a piece of text.
+ */
+ public static final int TEXT = 1;
+
+ /**
+ * Indicates ths item represents an icon.
+ */
+ public static final int ICON = 2;
+
+ /**
+ * The type of this item. One of TEXT, ICON, or LEVEL_ICON.
+ */
+ public int type;
+
+ /**
+ * The slot that this icon will be in if it is not a notification
+ */
+ public String slot;
+
+ /**
+ * The package containting the icon to draw for this item. Valid if this is
+ * an ICON type.
+ */
+ public String iconPackage;
+
+ /**
+ * The icon to draw for this item. Valid if this is an ICON type.
+ */
+ public int iconId;
+
+ /**
+ * The level associated with the icon. Valid if this is a LEVEL_ICON type.
+ */
+ public int iconLevel;
+
+ /**
+ * The "count" number.
+ */
+ public int number;
+
+ /**
+ * The text associated with the icon. Valid if this is a TEXT type.
+ */
+ public CharSequence text;
+
+ private IconData() {
+ }
+
+ public static IconData makeIcon(String slot,
+ String iconPackage, int iconId, int iconLevel, int number) {
+ IconData data = new IconData();
+ data.type = ICON;
+ data.slot = slot;
+ data.iconPackage = iconPackage;
+ data.iconId = iconId;
+ data.iconLevel = iconLevel;
+ data.number = number;
+ return data;
+ }
+
+ public static IconData makeText(String slot, CharSequence text) {
+ IconData data = new IconData();
+ data.type = TEXT;
+ data.slot = slot;
+ data.text = text;
+ return data;
+ }
+
+ public void copyFrom(IconData that) {
+ this.type = that.type;
+ this.slot = that.slot;
+ this.iconPackage = that.iconPackage;
+ this.iconId = that.iconId;
+ this.iconLevel = that.iconLevel;
+ this.number = that.number;
+ this.text = that.text; // should we clone this?
+ }
+
+ public IconData clone() {
+ IconData that = new IconData();
+ that.copyFrom(this);
+ return that;
+ }
+
+ public String toString() {
+ if (this.type == TEXT) {
+ return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
+ + " text='" + this.text + "')";
+ }
+ else if (this.type == ICON) {
+ return "IconData(slot=" + (this.slot != null ? "'" + this.slot + "'" : "null")
+ + " package=" + this.iconPackage
+ + " iconId=" + Integer.toHexString(this.iconId)
+ + " iconLevel=" + this.iconLevel + ")";
+ }
+ else {
+ return "IconData(type=" + type + ")";
+ }
+ }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconMerger.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconMerger.java
new file mode 100644
index 0000000..8fcd36f
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/IconMerger.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+public class IconMerger extends LinearLayout {
+ PhoneStatusBarService service;
+ StatusBarIcon moreIcon;
+
+ public IconMerger(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+
+ final int maxWidth = r - l;
+ final int N = getChildCount();
+ int i;
+
+ // get the rightmost one, and see if we even need to do anything
+ int fitRight = -1;
+ for (i=N-1; i>=0; i--) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ fitRight = child.getRight();
+ break;
+ }
+ }
+
+ // find the first visible one that isn't the more icon
+ View moreView = null;
+ int fitLeft = -1;
+ int startIndex = -1;
+ for (i=0; i<N; i++) {
+ final View child = getChildAt(i);
+ if (com.android.internal.R.drawable.stat_notify_more == child.getId()) {
+ moreView = child;
+ startIndex = i+1;
+ }
+ else if (child.getVisibility() != GONE) {
+ fitLeft = child.getLeft();
+ break;
+ }
+ }
+
+ if (moreView == null || startIndex < 0) {
+ throw new RuntimeException("Status Bar / IconMerger moreView == null");
+ }
+
+ // if it fits without the more icon, then hide the more icon and update fitLeft
+ // so everything gets pushed left
+ int adjust = 0;
+ if (fitRight - fitLeft <= maxWidth) {
+ adjust = fitLeft - moreView.getLeft();
+ fitLeft -= adjust;
+ fitRight -= adjust;
+ moreView.layout(0, moreView.getTop(), 0, moreView.getBottom());
+ }
+ int extra = fitRight - r;
+ int shift = -1;
+
+ int breakingPoint = fitLeft + extra + adjust;
+ int number = 0;
+ for (i=startIndex; i<N; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() != GONE) {
+ int childLeft = child.getLeft();
+ int childRight = child.getRight();
+ if (childLeft < breakingPoint) {
+ // hide this one
+ child.layout(0, child.getTop(), 0, child.getBottom());
+ int n = this.service.getIconNumberForView(child);
+ if (n == 0) {
+ number += 1;
+ } else if (n > 0) {
+ number += n;
+ }
+ } else {
+ // decide how much to shift by
+ if (shift < 0) {
+ shift = childLeft - fitLeft;
+ }
+ // shift this left by shift
+ child.layout(childLeft-shift, child.getTop(),
+ childRight-shift, child.getBottom());
+ }
+ }
+ }
+
+ // BUG: Updating the text during the layout here doesn't seem to cause
+ // the view to be redrawn fully. The text view gets resized correctly, but the
+ // text contents aren't drawn properly. To work around this, we post a message
+ // and provide the value later. We're the only one changing this value show it
+ // should be ordered correctly.
+ if (false) {
+ this.moreIcon.update(number);
+ } else {
+ mBugWorkaroundNumber = number;
+ mBugWorkaroundHandler.post(mBugWorkaroundRunnable);
+ }
+ }
+
+ private int mBugWorkaroundNumber;
+ private Handler mBugWorkaroundHandler = new Handler();
+ private Runnable mBugWorkaroundRunnable = new Runnable() {
+ public void run() {
+ IconMerger.this.moreIcon.update(mBugWorkaroundNumber);
+ IconMerger.this.moreIcon.view.invalidate();
+ }
+ };
+}
diff --git a/services/java/com/android/server/status/TickerView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java
similarity index 66%
copy from services/java/com/android/server/status/TickerView.java
copy to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java
index 099dffb..36e1bfb 100644
--- a/services/java/com/android/server/status/TickerView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java
@@ -14,25 +14,21 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.policy.statusbar.phone;
import android.content.Context;
import android.util.AttributeSet;
-import android.widget.TextSwitcher;
+import android.util.Slog;
+import android.view.MotionEvent;
+import android.widget.FrameLayout;
+public class LatestItemView extends FrameLayout {
-public class TickerView extends TextSwitcher
-{
- Ticker mTicker;
-
- public TickerView(Context context, AttributeSet attrs) {
+ public LatestItemView(Context context, AttributeSet attrs) {
super(context, attrs);
}
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mTicker.reflowText();
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ return onTouchEvent(ev);
}
}
-
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationData.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationData.java
new file mode 100644
index 0000000..ca2d79b
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationData.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.app.PendingIntent;
+import android.widget.RemoteViews;
+
+public class NotificationData {
+ public String pkg;
+ public String tag;
+ public int id;
+ public CharSequence tickerText;
+
+ public long when;
+ public boolean ongoingEvent;
+ public boolean clearable;
+
+ public RemoteViews contentView;
+ public PendingIntent contentIntent;
+
+ public PendingIntent deleteIntent;
+
+ public String toString() {
+ return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText
+ + " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
+ + " deleteIntent=" + deleteIntent
+ + " clearable=" + clearable
+ + " contentView=" + contentView + " when=" + when + ")";
+ }
+}
diff --git a/services/java/com/android/server/status/TickerView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java
similarity index 65%
copy from services/java/com/android/server/status/TickerView.java
copy to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java
index 099dffb..a5d0c6a 100644
--- a/services/java/com/android/server/status/TickerView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java
@@ -14,25 +14,16 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.policy.statusbar.phone;
import android.content.Context;
import android.util.AttributeSet;
-import android.widget.TextSwitcher;
+import android.widget.LinearLayout;
-public class TickerView extends TextSwitcher
-{
- Ticker mTicker;
-
- public TickerView(Context context, AttributeSet attrs) {
+public class NotificationLinearLayout extends LinearLayout {
+ public NotificationLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- mTicker.reflowText();
- }
}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationViewList.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationViewList.java
new file mode 100644
index 0000000..300d58b
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationViewList.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.View;
+import java.util.ArrayList;
+
+public class NotificationViewList {
+ private ArrayList<StatusBarNotification> mOngoing = new ArrayList();
+ private ArrayList<StatusBarNotification> mLatest = new ArrayList();
+
+ public NotificationViewList() {
+ }
+
+ private static final int indexInList(ArrayList<StatusBarNotification> list, NotificationData n){
+ final int N = list.size();
+ for (int i=0; i<N; i++) {
+ StatusBarNotification that = list.get(i);
+ if (that.data == n) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ int getIconIndex(NotificationData n) {
+ final int ongoingSize = mOngoing.size();
+ final int latestSize = mLatest.size();
+ if (n.ongoingEvent) {
+ int index = indexInList(mOngoing, n);
+ if (index >= 0) {
+ return latestSize + index + 1;
+ } else {
+ return -1;
+ }
+ } else {
+ return indexInList(mLatest, n) + 1;
+ }
+ }
+
+ void remove(StatusBarNotification notification) {
+ NotificationData n = notification.data;
+ int index;
+ index = indexInList(mOngoing, n);
+ if (index >= 0) {
+ mOngoing.remove(index);
+ return;
+ }
+ index = indexInList(mLatest, n);
+ if (index >= 0) {
+ mLatest.remove(index);
+ return;
+ }
+ }
+
+ ArrayList<StatusBarNotification> notificationsForPackage(String packageName) {
+ ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>();
+ int N = mOngoing.size();
+ for (int i=0; i<N; i++) {
+ if (matchPackage(mOngoing.get(i), packageName)) {
+ list.add(mOngoing.get(i));
+ }
+ }
+ N = mLatest.size();
+ for (int i=0; i<N; i++) {
+ if (matchPackage(mLatest.get(i), packageName)) {
+ list.add(mLatest.get(i));
+ }
+ }
+ return list;
+ }
+
+ private final boolean matchPackage(StatusBarNotification snb, String packageName) {
+ if (snb.data.contentIntent != null) {
+ if (snb.data.contentIntent.getTargetPackage().equals(packageName)) {
+ return true;
+ }
+ } else if (snb.data.pkg != null && snb.data.pkg.equals(packageName)) {
+ return true;
+ }
+ return false;
+ }
+
+ private static final int indexForKey(ArrayList<StatusBarNotification> list, IBinder key) {
+ final int N = list.size();
+ for (int i=0; i<N; i++) {
+ if (list.get(i).key == key) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ StatusBarNotification get(IBinder key) {
+ int index;
+ index = indexForKey(mOngoing, key);
+ if (index >= 0) {
+ return mOngoing.get(index);
+ }
+ index = indexForKey(mLatest, key);
+ if (index >= 0) {
+ return mLatest.get(index);
+ }
+ return null;
+ }
+
+ // gets the index of the notification's view in its expanded parent view
+ int getExpandedIndex(StatusBarNotification notification) {
+ ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
+ final IBinder key = notification.key;
+ int index = 0;
+ // (the view order is backwards from this list order)
+ for (int i=list.size()-1; i>=0; i--) {
+ StatusBarNotification item = list.get(i);
+ if (item.key == key) {
+ return index;
+ }
+ if (item.view != null) {
+ index++;
+ }
+ }
+ Slog.e(PhoneStatusBarService.TAG, "Couldn't find notification in NotificationViewList.");
+ Slog.e(PhoneStatusBarService.TAG, "notification=" + notification);
+ dump(notification);
+ return 0;
+ }
+
+ void clearViews() {
+ int N = mOngoing.size();
+ for (int i=0; i<N; i++) {
+ mOngoing.get(i).view = null;
+ }
+ N = mLatest.size();
+ for (int i=0; i<N; i++) {
+ mLatest.get(i).view = null;
+ }
+ }
+
+ int ongoingCount() {
+ return mOngoing.size();
+ }
+
+ int latestCount() {
+ return mLatest.size();
+ }
+
+ StatusBarNotification getOngoing(int index) {
+ return mOngoing.get(index);
+ }
+
+ StatusBarNotification getLatest(int index) {
+ return mLatest.get(index);
+ }
+
+ int size() {
+ return mOngoing.size() + mLatest.size();
+ }
+
+ void add(StatusBarNotification notification) {
+ if (PhoneStatusBarService.SPEW) {
+ Slog.d(PhoneStatusBarService.TAG, "before add NotificationViewList"
+ + " notification.data.ongoingEvent=" + notification.data.ongoingEvent);
+ dump(notification);
+ }
+
+ ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
+ long when = notification.data.when;
+ final int N = list.size();
+ int index = N;
+ for (int i=0; i<N; i++) {
+ StatusBarNotification that = list.get(i);
+ if (that.data.when > when) {
+ index = i;
+ break;
+ }
+ }
+ list.add(index, notification);
+
+ if (PhoneStatusBarService.SPEW) {
+ Slog.d(PhoneStatusBarService.TAG, "after add NotificationViewList index=" + index);
+ dump(notification);
+ }
+ }
+
+ void dump(StatusBarNotification notification) {
+ if (PhoneStatusBarService.SPEW) {
+ boolean showTime = false;
+ String s = "";
+ for (int i=0; i<mOngoing.size(); i++) {
+ StatusBarNotification that = mOngoing.get(i);
+ if (that.key == notification.key) {
+ s += "[";
+ }
+ if (showTime) {
+ s += that.data.when;
+ } else {
+ s += that.data.pkg + "/" + that.data.id + "/" + that.view;
+ }
+ if (that.key == notification.key) {
+ s += "]";
+ }
+ s += " ";
+ }
+ Slog.d(PhoneStatusBarService.TAG, "NotificationViewList ongoing: " + s);
+
+ s = "";
+ for (int i=0; i<mLatest.size(); i++) {
+ StatusBarNotification that = mLatest.get(i);
+ if (that.key == notification.key) {
+ s += "[";
+ }
+ if (showTime) {
+ s += that.data.when;
+ } else {
+ s += that.data.pkg + "/" + that.data.id + "/" + that.view;
+ }
+ if (that.key == notification.key) {
+ s += "]";
+ }
+ s += " ";
+ }
+ Slog.d(PhoneStatusBarService.TAG, "NotificationViewList latest: " + s);
+ }
+ }
+
+ StatusBarNotification get(View view) {
+ int N = mOngoing.size();
+ for (int i=0; i<N; i++) {
+ StatusBarNotification notification = mOngoing.get(i);
+ View v = notification.view;
+ if (v == view) {
+ return notification;
+ }
+ }
+ N = mLatest.size();
+ for (int i=0; i<N; i++) {
+ StatusBarNotification notification = mLatest.get(i);
+ View v = notification.view;
+ if (v == view) {
+ return notification;
+ }
+ }
+ return null;
+ }
+
+ void update(StatusBarNotification notification) {
+ remove(notification);
+ add(notification);
+ }
+
+ boolean hasClearableItems() {
+ int N = mLatest.size();
+ for (int i=0; i<N; i++) {
+ if (mLatest.get(i).data.clearable) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
index b9b38db..daed2ef 100644
--- a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/PhoneStatusBarService.java
@@ -16,25 +16,306 @@
package com.android.policy.statusbar.phone;
-import android.app.Service;
+import com.android.internal.util.CharSequences;
+
+import android.app.ActivityManagerNative;
+import android.app.Dialog;
import android.app.IStatusBar;
import android.app.IStatusBarService;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
import android.os.SystemClock;
-import android.util.Log;
+import android.provider.Telephony;
+import android.util.Slog;
+import android.view.Display;
import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.LinearLayout;
+import android.widget.RemoteViews;
+import android.widget.ScrollView;
+import android.widget.TextView;
+import android.widget.FrameLayout;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
public class PhoneStatusBarService extends StatusBarService {
+ static final String TAG = "StatusBar";
+ static final boolean SPEW = false;
+
+ public static final String ACTION_STATUSBAR_START
+ = "com.android.internal.policy.statusbar.START";
+
+ static final int EXPANDED_LEAVE_ALONE = -10000;
+ static final int EXPANDED_FULL_OPEN = -10001;
+
+ private static final int MSG_ANIMATE = 1000;
+ private static final int MSG_ANIMATE_REVEAL = 1001;
+
+ private static final int OP_ADD_ICON = 1;
+ private static final int OP_UPDATE_ICON = 2;
+ private static final int OP_REMOVE_ICON = 3;
+ private static final int OP_SET_VISIBLE = 4;
+ private static final int OP_EXPAND = 5;
+ private static final int OP_TOGGLE = 6;
+ private static final int OP_DISABLE = 7;
+ private class PendingOp {
+ IBinder key;
+ int code;
+ IconData iconData;
+ NotificationData notificationData;
+ boolean visible;
+ int integer;
+ }
+
+ private class DisableRecord implements IBinder.DeathRecipient {
+ String pkg;
+ int what;
+ IBinder token;
+
+ public void binderDied() {
+ Slog.i(TAG, "binder died for pkg=" + pkg);
+ disable(0, token, pkg);
+ token.unlinkToDeath(this, 0);
+ }
+ }
+
+ public interface NotificationCallbacks {
+ void onSetDisabled(int status);
+ void onClearAll();
+ void onNotificationClick(String pkg, String tag, int id);
+ void onPanelRevealed();
+ }
+
+ private class ExpandedDialog extends Dialog {
+ ExpandedDialog(Context context) {
+ super(context, com.android.internal.R.style.Theme_Light_NoTitleBar);
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_BACK:
+ if (!down) {
+ PhoneStatusBarService.this.deactivate();
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+ }
+
+ final Display mDisplay;
+ StatusBarView mStatusBarView;
+ int mPixelFormat;
+ H mHandler = new H();
+ Object mQueueLock = new Object();
+ ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
+ NotificationCallbacks mNotificationCallbacks;
+
+ // All accesses to mIconMap and mNotificationData are syncronized on those objects,
+ // but this is only so dump() can work correctly. Modifying these outside of the UI
+ // thread will not work, there are places in the code that unlock and reaquire between
+ // reads and require them to not be modified.
+
+ // icons
+ HashMap<IBinder,StatusBarIcon> mIconMap = new HashMap<IBinder,StatusBarIcon>();
+ ArrayList<StatusBarIcon> mIconList = new ArrayList<StatusBarIcon>();
+ String[] mRightIconSlots;
+ StatusBarIcon[] mRightIcons;
+ LinearLayout mIcons;
+ IconMerger mNotificationIcons;
+ LinearLayout mStatusIcons;
+ StatusBarIcon mMoreIcon;
+ private UninstallReceiver mUninstallReceiver;
+
+ // expanded notifications
+ NotificationViewList mNotificationData = new NotificationViewList();
+ Dialog mExpandedDialog;
+ ExpandedView mExpandedView;
+ WindowManager.LayoutParams mExpandedParams;
+ ScrollView mScrollView;
+ View mNotificationLinearLayout;
+ TextView mOngoingTitle;
+ LinearLayout mOngoingItems;
+ TextView mLatestTitle;
+ LinearLayout mLatestItems;
+ TextView mNoNotificationsTitle;
+ TextView mSpnLabel;
+ TextView mPlmnLabel;
+ TextView mClearButton;
+ View mExpandedContents;
+ CloseDragHandle mCloseView;
+ int[] mPositionTmp = new int[2];
+ boolean mExpanded;
+ boolean mExpandedVisible;
+
+ // the date view
+ DateView mDateView;
+
+ // the tracker view
+ TrackingView mTrackingView;
+ WindowManager.LayoutParams mTrackingParams;
+ int mTrackingPosition; // the position of the top of the tracking view.
+
+ // ticker
+ private Ticker mTicker;
+ private View mTickerView;
+ private boolean mTicking;
+
+ // Tracking finger for opening/closing.
+ int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore
+ boolean mTracking;
+ VelocityTracker mVelocityTracker;
+
+ static final int ANIM_FRAME_DURATION = (1000/60);
+
+ boolean mAnimating;
+ long mCurAnimationTime;
+ float mDisplayHeight;
+ float mAnimY;
+ float mAnimVel;
+ float mAnimAccel;
+ long mAnimLastTime;
+ boolean mAnimatingReveal = false;
+ int mViewDelta;
+ int[] mAbsPos = new int[2];
+
+ // for disabling the status bar
+ ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
+ int mDisabled = 0;
+
+ /**
+ * Construct the service, add the status bar view to the window manager
+ */
+ public PhoneStatusBarService(Context context) {
+ mDisplay = ((WindowManager)context.getSystemService(
+ Context.WINDOW_SERVICE)).getDefaultDisplay();
+ makeStatusBarView(context);
+ mUninstallReceiver = new UninstallReceiver();
+ }
+
+ public void setNotificationCallbacks(NotificationCallbacks listener) {
+ mNotificationCallbacks = listener;
+ }
+
+ // ================================================================================
+ // Constructing the view
+ // ================================================================================
+ private void makeStatusBarView(Context context) {
+ Resources res = context.getResources();
+ mRightIconSlots = res.getStringArray(R.array.status_bar_icon_order);
+ mRightIcons = new StatusBarIcon[mRightIconSlots.length];
+
+ ExpandedView expanded = (ExpandedView)View.inflate(context,
+ com.android.internal.R.layout.status_bar_expanded, null);
+ expanded.mService = this;
+ StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
+ sb.mService = this;
+
+ // figure out which pixel-format to use for the status bar.
+ mPixelFormat = PixelFormat.TRANSLUCENT;
+ Drawable bg = sb.getBackground();
+ if (bg != null) {
+ mPixelFormat = bg.getOpacity();
+ }
+
+ mStatusBarView = sb;
+ mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
+ mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
+ mNotificationIcons.service = this;
+ mIcons = (LinearLayout)sb.findViewById(R.id.icons);
+ mTickerView = sb.findViewById(R.id.ticker);
+ mDateView = (DateView)sb.findViewById(R.id.date);
+
+ mExpandedDialog = new ExpandedDialog(context);
+ mExpandedView = expanded;
+ mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
+ mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
+ mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
+ mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
+ mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
+ mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
+ mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
+ mClearButton.setOnClickListener(mClearButtonListener);
+ mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel);
+ mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel);
+ mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
+ mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
+
+ mOngoingTitle.setVisibility(View.GONE);
+ mLatestTitle.setVisibility(View.GONE);
+
+ mTicker = new MyTicker(context, sb);
+
+ TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
+ tickerView.mTicker = mTicker;
+
+ mTrackingView = (TrackingView)View.inflate(context,
+ com.android.internal.R.layout.status_bar_tracking, null);
+ mTrackingView.mService = this;
+ mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
+ mCloseView.mService = this;
+
+ mEdgeBorder = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_edge_ignore);
+
+ // add the more icon for the notifications
+ IconData moreData = IconData.makeIcon(null, context.getPackageName(),
+ R.drawable.stat_notify_more, 0, 42);
+ mMoreIcon = new StatusBarIcon(context, moreData, mNotificationIcons);
+ mMoreIcon.view.setId(R.drawable.stat_notify_more);
+ mNotificationIcons.moreIcon = mMoreIcon;
+ mNotificationIcons.addView(mMoreIcon.view);
+
+ // set the inital view visibility
+ setAreThereNotifications();
+ mDateView.setVisibility(View.INVISIBLE);
+
+ // before we register for broadcasts
+ mPlmnLabel.setText(com.android.internal.R.string.lockscreen_carrier_default);
+ mPlmnLabel.setVisibility(View.VISIBLE);
+ mSpnLabel.setText("");
+ mSpnLabel.setVisibility(View.GONE);
+
+ // receive broadcasts
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
+ context.registerReceiver(mBroadcastReceiver, filter);
+ }
@Override
protected void addStatusBarView() {
@@ -55,4 +336,1538 @@
WindowManagerImpl.getDefault().addView(view, lp);
}
+
+ // ================================================================================
+ // From IStatusBarService
+ // ================================================================================
+ public void activate() {
+ enforceExpandStatusBar();
+ addPendingOp(OP_EXPAND, null, true);
+ }
+
+ public void deactivate() {
+ enforceExpandStatusBar();
+ addPendingOp(OP_EXPAND, null, false);
+ }
+
+ public void toggle() {
+ enforceExpandStatusBar();
+ addPendingOp(OP_TOGGLE, null, false);
+ }
+
+ public void disable(int what, IBinder token, String pkg) {
+ enforceStatusBar();
+ synchronized (mNotificationCallbacks) {
+ // This is a little gross, but I think it's safe as long as nobody else
+ // synchronizes on mNotificationCallbacks. It's important that the the callback
+ // and the pending op get done in the correct order and not interleaved with
+ // other calls, otherwise they'll get out of sync.
+ int net;
+ synchronized (mDisableRecords) {
+ manageDisableListLocked(what, token, pkg);
+ net = gatherDisableActionsLocked();
+ mNotificationCallbacks.onSetDisabled(net);
+ }
+ addPendingOp(OP_DISABLE, net);
+ }
+ }
+
+ public IBinder addIcon(String slot, String iconPackage, int iconId, int iconLevel) {
+ enforceStatusBar();
+ return addIcon(IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
+ }
+
+ public void updateIcon(IBinder key,
+ String slot, String iconPackage, int iconId, int iconLevel) {
+ enforceStatusBar();
+ updateIcon(key, IconData.makeIcon(slot, iconPackage, iconId, iconLevel, 0), null);
+ }
+
+ public void removeIcon(IBinder key) {
+ enforceStatusBar();
+ addPendingOp(OP_REMOVE_ICON, key, null, null, -1);
+ }
+
+ private void enforceStatusBar() {
+ enforceCallingOrSelfPermission(
+ android.Manifest.permission.STATUS_BAR,
+ "PhoneStatusBarService");
+ }
+
+ private void enforceExpandStatusBar() {
+ enforceCallingOrSelfPermission(
+ android.Manifest.permission.EXPAND_STATUS_BAR,
+ "PhoneStatusBarService");
+ }
+
+ public void registerStatusBar(IStatusBar bar) {
+ Slog.d(TAG, "registerStatusBar bar=" + bar);
+ }
+
+
+ // ================================================================================
+ // Can be called from any thread
+ // ================================================================================
+ public IBinder addIcon(IconData data, NotificationData n) {
+ // TODO: Call onto the IStatusBar
+ int slot;
+ // assert early-on if they using a slot that doesn't exist.
+ if (data != null && n == null) {
+ slot = getRightIconIndex(data.slot);
+ if (slot < 0) {
+ throw new SecurityException("invalid status bar icon slot: "
+ + (data.slot != null ? "'" + data.slot + "'" : "null"));
+ }
+ } else {
+ slot = -1;
+ }
+ IBinder key = new Binder();
+ addPendingOp(OP_ADD_ICON, key, data, n, -1);
+ return key;
+ }
+
+ public void updateIcon(IBinder key, IconData data, NotificationData n) {
+ addPendingOp(OP_UPDATE_ICON, key, data, n, -1);
+ }
+
+ public void setIconVisibility(IBinder key, boolean visible) {
+ addPendingOp(OP_SET_VISIBLE, key, visible);
+ }
+
+ private void addPendingOp(int code, IBinder key, IconData data, NotificationData n, int i) {
+ synchronized (mQueueLock) {
+ PendingOp op = new PendingOp();
+ op.key = key;
+ op.code = code;
+ op.iconData = data == null ? null : data.clone();
+ op.notificationData = n;
+ op.integer = i;
+ mQueue.add(op);
+ if (mQueue.size() == 1) {
+ mHandler.sendEmptyMessage(2);
+ }
+ }
+ }
+
+ private void addPendingOp(int code, IBinder key, boolean visible) {
+ synchronized (mQueueLock) {
+ PendingOp op = new PendingOp();
+ op.key = key;
+ op.code = code;
+ op.visible = visible;
+ mQueue.add(op);
+ if (mQueue.size() == 1) {
+ mHandler.sendEmptyMessage(1);
+ }
+ }
+ }
+
+ private void addPendingOp(int code, int integer) {
+ synchronized (mQueueLock) {
+ PendingOp op = new PendingOp();
+ op.code = code;
+ op.integer = integer;
+ mQueue.add(op);
+ if (mQueue.size() == 1) {
+ mHandler.sendEmptyMessage(1);
+ }
+ }
+ }
+
+ // lock on mDisableRecords
+ void manageDisableListLocked(int what, IBinder token, String pkg) {
+ if (SPEW) {
+ Slog.d(TAG, "manageDisableList what=0x" + Integer.toHexString(what)
+ + " pkg=" + pkg);
+ }
+ // update the list
+ synchronized (mDisableRecords) {
+ final int N = mDisableRecords.size();
+ DisableRecord tok = null;
+ int i;
+ for (i=0; i<N; i++) {
+ DisableRecord t = mDisableRecords.get(i);
+ if (t.token == token) {
+ tok = t;
+ break;
+ }
+ }
+ if (what == 0 || !token.isBinderAlive()) {
+ if (tok != null) {
+ mDisableRecords.remove(i);
+ tok.token.unlinkToDeath(tok, 0);
+ }
+ } else {
+ if (tok == null) {
+ tok = new DisableRecord();
+ try {
+ token.linkToDeath(tok, 0);
+ }
+ catch (RemoteException ex) {
+ return; // give up
+ }
+ mDisableRecords.add(tok);
+ }
+ tok.what = what;
+ tok.token = token;
+ tok.pkg = pkg;
+ }
+ }
+ }
+
+ // lock on mDisableRecords
+ int gatherDisableActionsLocked() {
+ final int N = mDisableRecords.size();
+ // gather the new net flags
+ int net = 0;
+ for (int i=0; i<N; i++) {
+ net |= mDisableRecords.get(i).what;
+ }
+ return net;
+ }
+
+ private int getRightIconIndex(String slot) {
+ final int N = mRightIconSlots.length;
+ for (int i=0; i<N; i++) {
+ if (mRightIconSlots[i].equals(slot)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ // ================================================================================
+ // Always called from UI thread
+ // ================================================================================
+ /**
+ * All changes to the status bar and notifications funnel through here and are batched.
+ */
+ private class H extends Handler {
+ public void handleMessage(Message m) {
+ if (m.what == MSG_ANIMATE) {
+ doAnimation();
+ return;
+ }
+ if (m.what == MSG_ANIMATE_REVEAL) {
+ doRevealAnimation();
+ return;
+ }
+
+ ArrayList<PendingOp> queue;
+ synchronized (mQueueLock) {
+ queue = mQueue;
+ mQueue = new ArrayList<PendingOp>();
+ }
+
+ boolean wasExpanded = mExpanded;
+
+ // for each one in the queue, find all of the ones with the same key
+ // and collapse that down into a final op and/or call to setVisibility, etc
+ boolean expand = wasExpanded;
+ boolean doExpand = false;
+ boolean doDisable = false;
+ int disableWhat = 0;
+ int N = queue.size();
+ while (N > 0) {
+ PendingOp op = queue.get(0);
+ boolean doOp = false;
+ boolean visible = false;
+ boolean doVisibility = false;
+ if (op.code == OP_SET_VISIBLE) {
+ doVisibility = true;
+ visible = op.visible;
+ }
+ else if (op.code == OP_EXPAND) {
+ doExpand = true;
+ expand = op.visible;
+ }
+ else if (op.code == OP_TOGGLE) {
+ doExpand = true;
+ expand = !expand;
+ }
+ else {
+ doOp = true;
+ }
+
+ if (alwaysHandle(op.code)) {
+ // coalesce these
+ for (int i=1; i<N; i++) {
+ PendingOp o = queue.get(i);
+ if (!alwaysHandle(o.code) && o.key == op.key) {
+ if (o.code == OP_SET_VISIBLE) {
+ visible = o.visible;
+ doVisibility = true;
+ }
+ else if (o.code == OP_EXPAND) {
+ expand = o.visible;
+ doExpand = true;
+ }
+ else {
+ op.code = o.code;
+ op.iconData = o.iconData;
+ op.notificationData = o.notificationData;
+ }
+ queue.remove(i);
+ i--;
+ N--;
+ }
+ }
+ }
+
+ queue.remove(0);
+ N--;
+
+ if (doOp) {
+ switch (op.code) {
+ case OP_ADD_ICON:
+ case OP_UPDATE_ICON:
+ performAddUpdateIcon(op.key, op.iconData, op.notificationData);
+ break;
+ case OP_REMOVE_ICON:
+ performRemoveIcon(op.key);
+ break;
+ case OP_DISABLE:
+ doDisable = true;
+ disableWhat = op.integer;
+ break;
+ }
+ }
+ if (doVisibility && op.code != OP_REMOVE_ICON) {
+ performSetIconVisibility(op.key, visible);
+ }
+ }
+
+ if (queue.size() != 0) {
+ throw new RuntimeException("Assertion failed: queue.size=" + queue.size());
+ }
+ if (doExpand) {
+ // this is last so that we capture all of the pending changes before doing it
+ if (expand) {
+ animateExpand();
+ } else {
+ animateCollapse();
+ }
+ }
+ if (doDisable) {
+ performDisableActions(disableWhat);
+ }
+ }
+ }
+
+ private boolean alwaysHandle(int code) {
+ return code == OP_DISABLE;
+ }
+
+ /* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
+ throws StatusBarException {
+ if (SPEW) {
+ Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
+ }
+ // notification
+ if (n != null) {
+ StatusBarNotification notification = getNotification(key);
+ NotificationData oldData = null;
+ if (notification == null) {
+ // add
+ notification = new StatusBarNotification();
+ notification.key = key;
+ notification.data = n;
+ synchronized (mNotificationData) {
+ mNotificationData.add(notification);
+ }
+ addNotificationView(notification);
+ setAreThereNotifications();
+ } else {
+ // update
+ oldData = notification.data;
+ notification.data = n;
+ updateNotificationView(notification, oldData);
+ }
+ // Show the ticker if one is requested, and the text is different
+ // than the currently displayed ticker. Also don't do this
+ // until status bar window is attached to the window manager,
+ // because... well, what's the point otherwise? And trying to
+ // run a ticker without being attached will crash!
+ if (n.tickerText != null && mStatusBarView.getWindowToken() != null
+ && (oldData == null
+ || oldData.tickerText == null
+ || !CharSequences.equals(oldData.tickerText, n.tickerText))) {
+ if (0 == (mDisabled &
+ (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
+ mTicker.addEntry(n, StatusBarIcon.getIcon(this, data), n.tickerText);
+ }
+ }
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ }
+
+ // icon
+ synchronized (mIconMap) {
+ StatusBarIcon icon = mIconMap.get(key);
+ if (icon == null) {
+ // add
+ LinearLayout v = n == null ? mStatusIcons : mNotificationIcons;
+
+ icon = new StatusBarIcon(this, data, v);
+ mIconMap.put(key, icon);
+ mIconList.add(icon);
+
+ if (n == null) {
+ int slotIndex = getRightIconIndex(data.slot);
+ StatusBarIcon[] rightIcons = mRightIcons;
+ if (rightIcons[slotIndex] == null) {
+ int pos = 0;
+ for (int i=mRightIcons.length-1; i>slotIndex; i--) {
+ StatusBarIcon ic = rightIcons[i];
+ if (ic != null) {
+ pos++;
+ }
+ }
+ rightIcons[slotIndex] = icon;
+ mStatusIcons.addView(icon.view, pos);
+ } else {
+ Slog.e(TAG, "duplicate icon in slot " + slotIndex + "/" + data.slot);
+ mIconMap.remove(key);
+ mIconList.remove(icon);
+ return ;
+ }
+ } else {
+ int iconIndex = mNotificationData.getIconIndex(n);
+ mNotificationIcons.addView(icon.view, iconIndex);
+ }
+ } else {
+ if (n == null) {
+ // right hand side icons -- these don't reorder
+ icon.update(this, data);
+ } else {
+ // remove old
+ ViewGroup parent = (ViewGroup)icon.view.getParent();
+ parent.removeView(icon.view);
+ // add new
+ icon.update(this, data);
+ int iconIndex = mNotificationData.getIconIndex(n);
+ mNotificationIcons.addView(icon.view, iconIndex);
+ }
+ }
+ }
+ }
+
+ /* private */ void performSetIconVisibility(IBinder key, boolean visible) {
+ synchronized (mIconMap) {
+ if (SPEW) {
+ Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
+ }
+ StatusBarIcon icon = mIconMap.get(key);
+ icon.view.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+ }
+
+ /* private */ void performRemoveIcon(IBinder key) {
+ synchronized (this) {
+ if (SPEW) {
+ Slog.d(TAG, "performRemoveIcon key=" + key);
+ }
+ StatusBarIcon icon = mIconMap.remove(key);
+ mIconList.remove(icon);
+ if (icon != null) {
+ ViewGroup parent = (ViewGroup)icon.view.getParent();
+ parent.removeView(icon.view);
+ int slotIndex = getRightIconIndex(icon.mData.slot);
+ if (slotIndex >= 0) {
+ mRightIcons[slotIndex] = null;
+ }
+ }
+ StatusBarNotification notification = getNotification(key);
+ if (notification != null) {
+ removeNotificationView(notification);
+ synchronized (mNotificationData) {
+ mNotificationData.remove(notification);
+ }
+ setAreThereNotifications();
+ }
+ }
+ }
+
+ int getIconNumberForView(View v) {
+ synchronized (mIconMap) {
+ StatusBarIcon icon = null;
+ final int N = mIconList.size();
+ for (int i=0; i<N; i++) {
+ StatusBarIcon ic = mIconList.get(i);
+ if (ic.view == v) {
+ icon = ic;
+ break;
+ }
+ }
+ if (icon != null) {
+ return icon.getNumber();
+ } else {
+ return -1;
+ }
+ }
+ }
+
+
+ StatusBarNotification getNotification(IBinder key) {
+ synchronized (mNotificationData) {
+ return mNotificationData.get(key);
+ }
+ }
+
+ View.OnFocusChangeListener mFocusChangeListener = new View.OnFocusChangeListener() {
+ public void onFocusChange(View v, boolean hasFocus) {
+ // Because 'v' is a ViewGroup, all its children will be (un)selected
+ // too, which allows marqueeing to work.
+ v.setSelected(hasFocus);
+ }
+ };
+
+ View makeNotificationView(StatusBarNotification notification, ViewGroup parent) {
+ NotificationData n = notification.data;
+ RemoteViews remoteViews = n.contentView;
+ if (remoteViews == null) {
+ return null;
+ }
+
+ // create the row view
+ LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View row = inflater.inflate(com.android.internal.R.layout.status_bar_latest_event, parent, false);
+
+ // bind the click event to the content area
+ ViewGroup content = (ViewGroup)row.findViewById(com.android.internal.R.id.content);
+ content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+ content.setOnFocusChangeListener(mFocusChangeListener);
+ PendingIntent contentIntent = n.contentIntent;
+ if (contentIntent != null) {
+ content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
+ }
+
+ View child = null;
+ Exception exception = null;
+ try {
+ child = remoteViews.apply(this, content);
+ }
+ catch (RuntimeException e) {
+ exception = e;
+ }
+ if (child == null) {
+ Slog.e(TAG, "couldn't inflate view for package " + n.pkg, exception);
+ return null;
+ }
+ content.addView(child);
+
+ row.setDrawingCacheEnabled(true);
+
+ notification.view = row;
+ notification.contentView = child;
+
+ return row;
+ }
+
+ void addNotificationView(StatusBarNotification notification) {
+ if (notification.view != null) {
+ throw new RuntimeException("Assertion failed: notification.view="
+ + notification.view);
+ }
+
+ LinearLayout parent = notification.data.ongoingEvent ? mOngoingItems : mLatestItems;
+
+ View child = makeNotificationView(notification, parent);
+ if (child == null) {
+ return ;
+ }
+
+ int index = mNotificationData.getExpandedIndex(notification);
+ parent.addView(child, index);
+ }
+
+ /**
+ * Remove the old one and put the new one in its place.
+ * @param notification the notification
+ */
+ void updateNotificationView(StatusBarNotification notification, NotificationData oldData) {
+ NotificationData n = notification.data;
+ if (oldData != null && n != null
+ && n.when == oldData.when
+ && n.ongoingEvent == oldData.ongoingEvent
+ && n.contentView != null && oldData.contentView != null
+ && n.contentView.getPackage() != null
+ && oldData.contentView.getPackage() != null
+ && oldData.contentView.getPackage().equals(n.contentView.getPackage())
+ && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
+ && notification.view != null) {
+ mNotificationData.update(notification);
+ try {
+ n.contentView.reapply(this, notification.contentView);
+
+ // update the contentIntent
+ ViewGroup content = (ViewGroup)notification.view.findViewById(
+ com.android.internal.R.id.content);
+ PendingIntent contentIntent = n.contentIntent;
+ if (contentIntent != null) {
+ content.setOnClickListener(new Launcher(contentIntent, n.pkg, n.tag, n.id));
+ }
+ }
+ catch (RuntimeException e) {
+ // It failed to add cleanly. Log, and remove the view from the panel.
+ Slog.w(TAG, "couldn't reapply views for package " + n.contentView.getPackage(), e);
+ removeNotificationView(notification);
+ }
+ } else {
+ mNotificationData.update(notification);
+ removeNotificationView(notification);
+ addNotificationView(notification);
+ }
+ setAreThereNotifications();
+ }
+
+ void removeNotificationView(StatusBarNotification notification) {
+ View v = notification.view;
+ if (v != null) {
+ ViewGroup parent = (ViewGroup)v.getParent();
+ parent.removeView(v);
+ notification.view = null;
+ }
+ }
+
+ private void setAreThereNotifications() {
+ boolean ongoing = mOngoingItems.getChildCount() != 0;
+ boolean latest = mLatestItems.getChildCount() != 0;
+
+ if (mNotificationData.hasClearableItems()) {
+ mClearButton.setVisibility(View.VISIBLE);
+ } else {
+ mClearButton.setVisibility(View.INVISIBLE);
+ }
+
+ mOngoingTitle.setVisibility(ongoing ? View.VISIBLE : View.GONE);
+ mLatestTitle.setVisibility(latest ? View.VISIBLE : View.GONE);
+
+ if (ongoing || latest) {
+ mNoNotificationsTitle.setVisibility(View.GONE);
+ } else {
+ mNoNotificationsTitle.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void makeExpandedVisible() {
+ if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible);
+ if (mExpandedVisible) {
+ return;
+ }
+ mExpandedVisible = true;
+ panelSlightlyVisible(true);
+
+ updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ mExpandedView.requestFocus(View.FOCUS_FORWARD);
+ mTrackingView.setVisibility(View.VISIBLE);
+
+ if (!mTicking) {
+ setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+
+ void animateExpand() {
+ if (SPEW) Slog.d(TAG, "Animate expand: expanded=" + mExpanded);
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return ;
+ }
+ if (mExpanded) {
+ return;
+ }
+
+ prepareTracking(0, true);
+ performFling(0, 2000.0f, true);
+ }
+
+ void animateCollapse() {
+ if (SPEW) {
+ Slog.d(TAG, "animateCollapse(): mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible
+ + " mExpanded=" + mExpanded
+ + " mAnimating=" + mAnimating
+ + " mAnimY=" + mAnimY
+ + " mAnimVel=" + mAnimVel);
+ }
+
+ if (!mExpandedVisible) {
+ return;
+ }
+
+ int y;
+ if (mAnimating) {
+ y = (int)mAnimY;
+ } else {
+ y = mDisplay.getHeight()-1;
+ }
+ // Let the fling think that we're open so it goes in the right direction
+ // and doesn't try to re-open the windowshade.
+ mExpanded = true;
+ prepareTracking(y, false);
+ performFling(y, -2000.0f, true);
+ }
+
+ void performExpand() {
+ if (SPEW) Slog.d(TAG, "performExpand: mExpanded=" + mExpanded);
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return ;
+ }
+ if (mExpanded) {
+ return;
+ }
+
+ // It seems strange to sometimes not expand...
+ if (false) {
+ synchronized (mNotificationData) {
+ if (mNotificationData.size() == 0) {
+ return;
+ }
+ }
+ }
+
+ mExpanded = true;
+ makeExpandedVisible();
+ updateExpandedViewPos(EXPANDED_FULL_OPEN);
+
+ if (false) postStartTracing();
+ }
+
+ void performCollapse() {
+ if (SPEW) Slog.d(TAG, "performCollapse: mExpanded=" + mExpanded
+ + " mExpandedVisible=" + mExpandedVisible);
+
+ if (!mExpandedVisible) {
+ return;
+ }
+ mExpandedVisible = false;
+ panelSlightlyVisible(false);
+ mExpandedParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ mExpandedParams.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ mTrackingView.setVisibility(View.GONE);
+
+ if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ setDateViewVisibility(false, com.android.internal.R.anim.fade_out);
+
+ if (!mExpanded) {
+ return;
+ }
+ mExpanded = false;
+ }
+
+ void doAnimation() {
+ if (mAnimating) {
+ if (SPEW) Slog.d(TAG, "doAnimation");
+ if (SPEW) Slog.d(TAG, "doAnimation before mAnimY=" + mAnimY);
+ incrementAnim();
+ if (SPEW) Slog.d(TAG, "doAnimation after mAnimY=" + mAnimY);
+ if (mAnimY >= mDisplay.getHeight()-1) {
+ if (SPEW) Slog.d(TAG, "Animation completed to expanded state.");
+ mAnimating = false;
+ updateExpandedViewPos(EXPANDED_FULL_OPEN);
+ performExpand();
+ }
+ else if (mAnimY < mStatusBarView.getHeight()) {
+ if (SPEW) Slog.d(TAG, "Animation completed to collapsed state.");
+ mAnimating = false;
+ updateExpandedViewPos(0);
+ performCollapse();
+ }
+ else {
+ updateExpandedViewPos((int)mAnimY);
+ mCurAnimationTime += ANIM_FRAME_DURATION;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+ }
+ }
+ }
+
+ void stopTracking() {
+ mTracking = false;
+ mVelocityTracker.recycle();
+ mVelocityTracker = null;
+ }
+
+ void incrementAnim() {
+ long now = SystemClock.uptimeMillis();
+ float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
+ final float y = mAnimY;
+ final float v = mAnimVel; // px/s
+ final float a = mAnimAccel; // px/s/s
+ mAnimY = y + (v*t) + (0.5f*a*t*t); // px
+ mAnimVel = v + (a*t); // px/s
+ mAnimLastTime = now; // ms
+ //Slog.d(TAG, "y=" + y + " v=" + v + " a=" + a + " t=" + t + " mAnimY=" + mAnimY
+ // + " mAnimAccel=" + mAnimAccel);
+ }
+
+ void doRevealAnimation() {
+ final int h = mCloseView.getHeight() + mStatusBarView.getHeight();
+ if (mAnimatingReveal && mAnimating && mAnimY < h) {
+ incrementAnim();
+ if (mAnimY >= h) {
+ mAnimY = h;
+ updateExpandedViewPos((int)mAnimY);
+ } else {
+ updateExpandedViewPos((int)mAnimY);
+ mCurAnimationTime += ANIM_FRAME_DURATION;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+ mCurAnimationTime);
+ }
+ }
+ }
+
+ void prepareTracking(int y, boolean opening) {
+ mTracking = true;
+ mVelocityTracker = VelocityTracker.obtain();
+ if (opening) {
+ mAnimAccel = 2000.0f;
+ mAnimVel = 200;
+ mAnimY = mStatusBarView.getHeight();
+ updateExpandedViewPos((int)mAnimY);
+ mAnimating = true;
+ mAnimatingReveal = true;
+ mHandler.removeMessages(MSG_ANIMATE);
+ mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+ long now = SystemClock.uptimeMillis();
+ mAnimLastTime = now;
+ mCurAnimationTime = now + ANIM_FRAME_DURATION;
+ mAnimating = true;
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE_REVEAL),
+ mCurAnimationTime);
+ makeExpandedVisible();
+ } else {
+ // it's open, close it?
+ if (mAnimating) {
+ mAnimating = false;
+ mHandler.removeMessages(MSG_ANIMATE);
+ }
+ updateExpandedViewPos(y + mViewDelta);
+ }
+ }
+
+ void performFling(int y, float vel, boolean always) {
+ mAnimatingReveal = false;
+ mDisplayHeight = mDisplay.getHeight();
+
+ mAnimY = y;
+ mAnimVel = vel;
+
+ //Slog.d(TAG, "starting with mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel);
+
+ if (mExpanded) {
+ if (!always && (
+ vel > 200.0f
+ || (y > (mDisplayHeight-25) && vel > -200.0f))) {
+ // We are expanded, but they didn't move sufficiently to cause
+ // us to retract. Animate back to the expanded position.
+ mAnimAccel = 2000.0f;
+ if (vel < 0) {
+ mAnimVel = 0;
+ }
+ }
+ else {
+ // We are expanded and are now going to animate away.
+ mAnimAccel = -2000.0f;
+ if (vel > 0) {
+ mAnimVel = 0;
+ }
+ }
+ } else {
+ if (always || (
+ vel > 200.0f
+ || (y > (mDisplayHeight/2) && vel > -200.0f))) {
+ // We are collapsed, and they moved enough to allow us to
+ // expand. Animate in the notifications.
+ mAnimAccel = 2000.0f;
+ if (vel < 0) {
+ mAnimVel = 0;
+ }
+ }
+ else {
+ // We are collapsed, but they didn't move sufficiently to cause
+ // us to retract. Animate back to the collapsed position.
+ mAnimAccel = -2000.0f;
+ if (vel > 0) {
+ mAnimVel = 0;
+ }
+ }
+ }
+ //Slog.d(TAG, "mAnimY=" + mAnimY + " mAnimVel=" + mAnimVel
+ // + " mAnimAccel=" + mAnimAccel);
+
+ long now = SystemClock.uptimeMillis();
+ mAnimLastTime = now;
+ mCurAnimationTime = now + ANIM_FRAME_DURATION;
+ mAnimating = true;
+ mHandler.removeMessages(MSG_ANIMATE);
+ mHandler.removeMessages(MSG_ANIMATE_REVEAL);
+ mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_ANIMATE), mCurAnimationTime);
+ stopTracking();
+ }
+
+ boolean interceptTouchEvent(MotionEvent event) {
+ if (SPEW) {
+ Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+ + mDisabled);
+ }
+
+ if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
+ return false;
+ }
+
+ final int statusBarSize = mStatusBarView.getHeight();
+ final int hitSize = statusBarSize*2;
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ final int y = (int)event.getRawY();
+
+ if (!mExpanded) {
+ mViewDelta = statusBarSize - y;
+ } else {
+ mTrackingView.getLocationOnScreen(mAbsPos);
+ mViewDelta = mAbsPos[1] + mTrackingView.getHeight() - y;
+ }
+ if ((!mExpanded && y < hitSize) ||
+ (mExpanded && y > (mDisplay.getHeight()-hitSize))) {
+
+ // We drop events at the edge of the screen to make the windowshade come
+ // down by accident less, especially when pushing open a device with a keyboard
+ // that rotates (like g1 and droid)
+ int x = (int)event.getRawX();
+ final int edgeBorder = mEdgeBorder;
+ if (x >= edgeBorder && x < mDisplay.getWidth() - edgeBorder) {
+ prepareTracking(y, !mExpanded);// opening if we're not already fully visible
+ mVelocityTracker.addMovement(event);
+ }
+ }
+ } else if (mTracking) {
+ mVelocityTracker.addMovement(event);
+ final int minY = statusBarSize + mCloseView.getHeight();
+ if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ int y = (int)event.getRawY();
+ if (mAnimatingReveal && y < minY) {
+ // nothing
+ } else {
+ mAnimatingReveal = false;
+ updateExpandedViewPos(y + mViewDelta);
+ }
+ } else if (event.getAction() == MotionEvent.ACTION_UP) {
+ mVelocityTracker.computeCurrentVelocity(1000);
+
+ float yVel = mVelocityTracker.getYVelocity();
+ boolean negative = yVel < 0;
+
+ float xVel = mVelocityTracker.getXVelocity();
+ if (xVel < 0) {
+ xVel = -xVel;
+ }
+ if (xVel > 150.0f) {
+ xVel = 150.0f; // limit how much we care about the x axis
+ }
+
+ float vel = (float)Math.hypot(yVel, xVel);
+ if (negative) {
+ vel = -vel;
+ }
+
+ performFling((int)event.getRawY(), vel, false);
+ }
+
+ }
+ return false;
+ }
+
+ private class Launcher implements View.OnClickListener {
+ private PendingIntent mIntent;
+ private String mPkg;
+ private String mTag;
+ private int mId;
+
+ Launcher(PendingIntent intent, String pkg, String tag, int id) {
+ mIntent = intent;
+ mPkg = pkg;
+ mTag = tag;
+ mId = id;
+ }
+
+ public void onClick(View v) {
+ try {
+ // The intent we are sending is for the application, which
+ // won't have permission to immediately start an activity after
+ // the user switches to home. We know it is safe to do at this
+ // point, so make sure new activity switches are now allowed.
+ ActivityManagerNative.getDefault().resumeAppSwitches();
+ } catch (RemoteException e) {
+ }
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ Intent overlay = new Intent();
+ overlay.setSourceBounds(
+ new Rect(pos[0], pos[1], pos[0]+v.getWidth(), pos[1]+v.getHeight()));
+ try {
+ mIntent.send(PhoneStatusBarService.this, 0, overlay);
+ mNotificationCallbacks.onNotificationClick(mPkg, mTag, mId);
+ } catch (PendingIntent.CanceledException e) {
+ // the stack trace isn't very helpful here. Just log the exception message.
+ Slog.w(TAG, "Sending contentIntent failed: " + e);
+ }
+ deactivate();
+ }
+ }
+
+ private class MyTicker extends Ticker {
+ MyTicker(Context context, StatusBarView sb) {
+ super(context, sb);
+ }
+
+ @Override
+ void tickerStarting() {
+ mTicking = true;
+ mIcons.setVisibility(View.GONE);
+ mTickerView.setVisibility(View.VISIBLE);
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
+ if (mExpandedVisible) {
+ setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
+ }
+ }
+
+ @Override
+ void tickerDone() {
+ mIcons.setVisibility(View.VISIBLE);
+ mTickerView.setVisibility(View.GONE);
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
+ mTickingDoneListener));
+ if (mExpandedVisible) {
+ setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
+ }
+ }
+
+ void tickerHalting() {
+ mIcons.setVisibility(View.VISIBLE);
+ mTickerView.setVisibility(View.GONE);
+ mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
+ mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
+ mTickingDoneListener));
+ if (mExpandedVisible) {
+ setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ }
+
+ Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
+ public void onAnimationEnd(Animation animation) {
+ mTicking = false;
+ }
+ public void onAnimationRepeat(Animation animation) {
+ }
+ public void onAnimationStart(Animation animation) {
+ }
+ };
+
+ private Animation loadAnim(int id, Animation.AnimationListener listener) {
+ Animation anim = AnimationUtils.loadAnimation(PhoneStatusBarService.this, id);
+ if (listener != null) {
+ anim.setAnimationListener(listener);
+ }
+ return anim;
+ }
+
+ public String viewInfo(View v) {
+ return "(" + v.getLeft() + "," + v.getTop() + ")(" + v.getRight() + "," + v.getBottom()
+ + " " + v.getWidth() + "x" + v.getHeight() + ")";
+ }
+
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump StatusBar from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ synchronized (mQueueLock) {
+ pw.println("Current Status Bar state:");
+ pw.println(" mExpanded=" + mExpanded
+ + ", mExpandedVisible=" + mExpandedVisible);
+ pw.println(" mTicking=" + mTicking);
+ pw.println(" mTracking=" + mTracking);
+ pw.println(" mAnimating=" + mAnimating
+ + ", mAnimY=" + mAnimY + ", mAnimVel=" + mAnimVel
+ + ", mAnimAccel=" + mAnimAccel);
+ pw.println(" mCurAnimationTime=" + mCurAnimationTime
+ + " mAnimLastTime=" + mAnimLastTime);
+ pw.println(" mDisplayHeight=" + mDisplayHeight
+ + " mAnimatingReveal=" + mAnimatingReveal
+ + " mViewDelta=" + mViewDelta);
+ pw.println(" mDisplayHeight=" + mDisplayHeight);
+ final int N = mQueue.size();
+ pw.println(" mQueue.size=" + N);
+ for (int i=0; i<N; i++) {
+ PendingOp op = mQueue.get(i);
+ pw.println(" [" + i + "] key=" + op.key + " code=" + op.code + " visible="
+ + op.visible);
+ pw.println(" iconData=" + op.iconData);
+ pw.println(" notificationData=" + op.notificationData);
+ }
+ pw.println(" mExpandedParams: " + mExpandedParams);
+ pw.println(" mExpandedView: " + viewInfo(mExpandedView));
+ pw.println(" mExpandedDialog: " + mExpandedDialog);
+ pw.println(" mTrackingParams: " + mTrackingParams);
+ pw.println(" mTrackingView: " + viewInfo(mTrackingView));
+ pw.println(" mOngoingTitle: " + viewInfo(mOngoingTitle));
+ pw.println(" mOngoingItems: " + viewInfo(mOngoingItems));
+ pw.println(" mLatestTitle: " + viewInfo(mLatestTitle));
+ pw.println(" mLatestItems: " + viewInfo(mLatestItems));
+ pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
+ pw.println(" mCloseView: " + viewInfo(mCloseView));
+ pw.println(" mTickerView: " + viewInfo(mTickerView));
+ pw.println(" mScrollView: " + viewInfo(mScrollView)
+ + " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
+ pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
+ }
+ synchronized (mIconMap) {
+ final int N = mIconMap.size();
+ pw.println(" mIconMap.size=" + N);
+ Set<IBinder> keys = mIconMap.keySet();
+ int i=0;
+ for (IBinder key: keys) {
+ StatusBarIcon icon = mIconMap.get(key);
+ pw.println(" [" + i + "] key=" + key);
+ pw.println(" data=" + icon.mData);
+ i++;
+ }
+ }
+ synchronized (mNotificationData) {
+ int N = mNotificationData.ongoingCount();
+ pw.println(" ongoingCount.size=" + N);
+ for (int i=0; i<N; i++) {
+ StatusBarNotification n = mNotificationData.getOngoing(i);
+ pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
+ pw.println(" data=" + n.data);
+ }
+ N = mNotificationData.latestCount();
+ pw.println(" ongoingCount.size=" + N);
+ for (int i=0; i<N; i++) {
+ StatusBarNotification n = mNotificationData.getLatest(i);
+ pw.println(" [" + i + "] key=" + n.key + " view=" + n.view);
+ pw.println(" data=" + n.data);
+ }
+ }
+ synchronized (mDisableRecords) {
+ final int N = mDisableRecords.size();
+ pw.println(" mDisableRecords.size=" + N
+ + " mDisabled=0x" + Integer.toHexString(mDisabled));
+ for (int i=0; i<N; i++) {
+ DisableRecord tok = mDisableRecords.get(i);
+ pw.println(" [" + i + "] what=0x" + Integer.toHexString(tok.what)
+ + " pkg=" + tok.pkg + " token=" + tok.token);
+ }
+ }
+
+ if (false) {
+ pw.println("see the logcat for a dump of the views we have created.");
+ // must happen on ui thread
+ mHandler.post(new Runnable() {
+ public void run() {
+ mStatusBarView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mStatusBarView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mStatusBarView.getWidth() + "x"
+ + mStatusBarView.getHeight());
+ mStatusBarView.debug();
+
+ mExpandedView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mExpandedView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mExpandedView.getWidth() + "x"
+ + mExpandedView.getHeight());
+ mExpandedView.debug();
+
+ mTrackingView.getLocationOnScreen(mAbsPos);
+ Slog.d(TAG, "mTrackingView: ----- (" + mAbsPos[0] + "," + mAbsPos[1]
+ + ") " + mTrackingView.getWidth() + "x"
+ + mTrackingView.getHeight());
+ mTrackingView.debug();
+ }
+ });
+ }
+ }
+
+ void onBarViewAttached() {
+ WindowManager.LayoutParams lp;
+ int pixelFormat;
+ Drawable bg;
+
+ /// ---------- Tracking View --------------
+ pixelFormat = PixelFormat.RGBX_8888;
+ bg = mTrackingView.getBackground();
+ if (bg != null) {
+ pixelFormat = bg.getOpacity();
+ }
+
+ lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ pixelFormat);
+// lp.token = mStatusBarView.getWindowToken();
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.setTitle("TrackingView");
+ lp.y = mTrackingPosition;
+ mTrackingParams = lp;
+
+ WindowManagerImpl.getDefault().addView(mTrackingView, lp);
+ }
+
+ void onTrackingViewAttached() {
+ WindowManager.LayoutParams lp;
+ int pixelFormat;
+ Drawable bg;
+
+ /// ---------- Expanded View --------------
+ pixelFormat = PixelFormat.TRANSLUCENT;
+
+ final int disph = mDisplay.getHeight();
+ lp = mExpandedDialog.getWindow().getAttributes();
+ lp.width = ViewGroup.LayoutParams.MATCH_PARENT;
+ lp.height = getExpandedHeight();
+ lp.x = 0;
+ mTrackingPosition = lp.y = -disph; // sufficiently large negative
+ lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
+ lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_DITHER
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ lp.format = pixelFormat;
+ lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
+ lp.setTitle("StatusBarExpanded");
+ mExpandedDialog.getWindow().setAttributes(lp);
+ mExpandedDialog.getWindow().setFormat(pixelFormat);
+ mExpandedParams = lp;
+
+ mExpandedDialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
+ mExpandedDialog.setContentView(mExpandedView,
+ new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT));
+ mExpandedDialog.getWindow().setBackgroundDrawable(null);
+ mExpandedDialog.show();
+ FrameLayout hack = (FrameLayout)mExpandedView.getParent();
+ }
+
+ void setDateViewVisibility(boolean visible, int anim) {
+ mDateView.setUpdates(visible);
+ mDateView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+ mDateView.startAnimation(loadAnim(anim, null));
+ }
+
+ void setNotificationIconVisibility(boolean visible, int anim) {
+ int old = mNotificationIcons.getVisibility();
+ int v = visible ? View.VISIBLE : View.INVISIBLE;
+ if (old != v) {
+ mNotificationIcons.setVisibility(v);
+ mNotificationIcons.startAnimation(loadAnim(anim, null));
+ }
+ }
+
+ void updateExpandedViewPos(int expandedPosition) {
+ if (SPEW) {
+ Slog.d(TAG, "updateExpandedViewPos before expandedPosition=" + expandedPosition
+ + " mTrackingParams.y=" + mTrackingParams.y
+ + " mTrackingPosition=" + mTrackingPosition);
+ }
+
+ int h = mStatusBarView.getHeight();
+ int disph = mDisplay.getHeight();
+
+ // If the expanded view is not visible, make sure they're still off screen.
+ // Maybe the view was resized.
+ if (!mExpandedVisible) {
+ if (mTrackingView != null) {
+ mTrackingPosition = -disph;
+ if (mTrackingParams != null) {
+ mTrackingParams.y = mTrackingPosition;
+ WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+ }
+ }
+ if (mExpandedParams != null) {
+ mExpandedParams.y = -disph;
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
+ return;
+ }
+
+ // tracking view...
+ int pos;
+ if (expandedPosition == EXPANDED_FULL_OPEN) {
+ pos = h;
+ }
+ else if (expandedPosition == EXPANDED_LEAVE_ALONE) {
+ pos = mTrackingPosition;
+ }
+ else {
+ if (expandedPosition <= disph) {
+ pos = expandedPosition;
+ } else {
+ pos = disph;
+ }
+ pos -= disph-h;
+ }
+ mTrackingPosition = mTrackingParams.y = pos;
+ mTrackingParams.height = disph-h;
+ WindowManagerImpl.getDefault().updateViewLayout(mTrackingView, mTrackingParams);
+
+ if (mExpandedParams != null) {
+ mCloseView.getLocationInWindow(mPositionTmp);
+ final int closePos = mPositionTmp[1];
+
+ mExpandedContents.getLocationInWindow(mPositionTmp);
+ final int contentsBottom = mPositionTmp[1] + mExpandedContents.getHeight();
+
+ mExpandedParams.y = pos + mTrackingView.getHeight()
+ - (mTrackingParams.height-closePos) - contentsBottom;
+ int max = h;
+ if (mExpandedParams.y > max) {
+ mExpandedParams.y = max;
+ }
+ int min = mTrackingPosition;
+ if (mExpandedParams.y < min) {
+ mExpandedParams.y = min;
+ }
+
+ boolean visible = (mTrackingPosition + mTrackingView.getHeight()) > h;
+ if (!visible) {
+ // if the contents aren't visible, move the expanded view way off screen
+ // because the window itself extends below the content view.
+ mExpandedParams.y = -disph;
+ }
+ panelSlightlyVisible(visible);
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
+
+ if (SPEW) {
+ Slog.d(TAG, "updateExpandedViewPos after expandedPosition=" + expandedPosition
+ + " mTrackingParams.y=" + mTrackingParams.y
+ + " mTrackingPosition=" + mTrackingPosition
+ + " mExpandedParams.y=" + mExpandedParams.y
+ + " mExpandedParams.height=" + mExpandedParams.height);
+ }
+ }
+
+ int getExpandedHeight() {
+ return mDisplay.getHeight() - mStatusBarView.getHeight() - mCloseView.getHeight();
+ }
+
+ void updateExpandedHeight() {
+ if (mExpandedView != null) {
+ mExpandedParams.height = getExpandedHeight();
+ mExpandedDialog.getWindow().setAttributes(mExpandedParams);
+ }
+ }
+
+ /**
+ * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+ * This was added last-minute and is inconsistent with the way the rest of the notifications
+ * are handled, because the notification isn't really cancelled. The lights are just
+ * turned off. If any other notifications happen, the lights will turn back on. Steve says
+ * this is what he wants. (see bug 1131461)
+ */
+ private boolean mPanelSlightlyVisible;
+ void panelSlightlyVisible(boolean visible) {
+ if (mPanelSlightlyVisible != visible) {
+ mPanelSlightlyVisible = visible;
+ if (visible) {
+ // tell the notification manager to turn off the lights.
+ mNotificationCallbacks.onPanelRevealed();
+ }
+ }
+ }
+
+ void performDisableActions(int net) {
+ int old = mDisabled;
+ int diff = net ^ old;
+ mDisabled = net;
+
+ // act accordingly
+ if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) {
+ if ((net & StatusBarManager.DISABLE_EXPAND) != 0) {
+ Slog.d(TAG, "DISABLE_EXPAND: yes");
+ animateCollapse();
+ }
+ }
+ if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ if ((net & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
+ if (mTicking) {
+ mNotificationIcons.setVisibility(View.INVISIBLE);
+ mTicker.halt();
+ } else {
+ setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
+ }
+ } else {
+ Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: no");
+ if (!mExpandedVisible) {
+ setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
+ }
+ }
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ mTicker.halt();
+ }
+ }
+ }
+
+ private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ mNotificationCallbacks.onClearAll();
+ addPendingOp(OP_EXPAND, null, false);
+ }
+ };
+
+ private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)
+ || Intent.ACTION_SCREEN_OFF.equals(action)) {
+ deactivate();
+ }
+ else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
+ updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
+ intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
+ intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
+ }
+ else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
+ updateResources();
+ }
+ }
+ };
+
+ void updateNetworkName(boolean showSpn, String spn, boolean showPlmn, String plmn) {
+ if (false) {
+ Slog.d(TAG, "updateNetworkName showSpn=" + showSpn + " spn=" + spn
+ + " showPlmn=" + showPlmn + " plmn=" + plmn);
+ }
+ boolean something = false;
+ if (showPlmn) {
+ mPlmnLabel.setVisibility(View.VISIBLE);
+ if (plmn != null) {
+ mPlmnLabel.setText(plmn);
+ } else {
+ mPlmnLabel.setText(com.android.internal.R.string.lockscreen_carrier_default);
+ }
+ } else {
+ mPlmnLabel.setText("");
+ mPlmnLabel.setVisibility(View.GONE);
+ }
+ if (showSpn && spn != null) {
+ mSpnLabel.setText(spn);
+ mSpnLabel.setVisibility(View.VISIBLE);
+ something = true;
+ } else {
+ mSpnLabel.setText("");
+ mSpnLabel.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * Reload some of our resources when the configuration changes.
+ *
+ * We don't reload everything when the configuration changes -- we probably
+ * should, but getting that smooth is tough. Someday we'll fix that. In the
+ * meantime, just update the things that we know change.
+ */
+ void updateResources() {
+ Resources res = getResources();
+
+ mClearButton.setText(getText(R.string.status_bar_clear_all_button));
+ mOngoingTitle.setText(getText(R.string.status_bar_ongoing_events_title));
+ mLatestTitle.setText(getText(R.string.status_bar_latest_events_title));
+ mNoNotificationsTitle.setText(getText(R.string.status_bar_no_notifications_title));
+
+ mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
+
+ if (false) Slog.v(TAG, "updateResources");
+ }
+
+ //
+ // tracing
+ //
+
+ void postStartTracing() {
+ mHandler.postDelayed(mStartTracing, 3000);
+ }
+
+ void vibrate() {
+ android.os.Vibrator vib = (android.os.Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
+ vib.vibrate(250);
+ }
+
+ Runnable mStartTracing = new Runnable() {
+ public void run() {
+ vibrate();
+ SystemClock.sleep(250);
+ Slog.d(TAG, "startTracing");
+ android.os.Debug.startMethodTracing("/data/statusbar-traces/trace");
+ mHandler.postDelayed(mStopTracing, 10000);
+ }
+ };
+
+ Runnable mStopTracing = new Runnable() {
+ public void run() {
+ android.os.Debug.stopMethodTracing();
+ Slog.d(TAG, "stopTracing");
+ vibrate();
+ }
+ };
+
+ class UninstallReceiver extends BroadcastReceiver {
+ public UninstallReceiver() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ filter.addDataScheme("package");
+ PhoneStatusBarService.this.registerReceiver(this, filter);
+ IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ PhoneStatusBarService.this.registerReceiver(this, sdFilter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String pkgList[] = null;
+ if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(intent.getAction())) {
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ } else {
+ Uri data = intent.getData();
+ if (data != null) {
+ String pkg = data.getSchemeSpecificPart();
+ if (pkg != null) {
+ pkgList = new String[]{pkg};
+ }
+ }
+ }
+ ArrayList<StatusBarNotification> list = null;
+ if (pkgList != null) {
+ synchronized (PhoneStatusBarService.this) {
+ for (String pkg : pkgList) {
+ list = mNotificationData.notificationsForPackage(pkg);
+ }
+ }
+ }
+
+ if (list != null) {
+ final int N = list.size();
+ for (int i=0; i<N; i++) {
+ removeIcon(list.get(i).key);
+ }
+ }
+ }
+ }
}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarException.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarException.java
new file mode 100644
index 0000000..1bb92f0
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+public class StatusBarException extends RuntimeException {
+ StatusBarException(String msg) {
+ super(msg);
+ }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIcon.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIcon.java
new file mode 100644
index 0000000..b48a17b
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarIcon.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class StatusBarIcon {
+ // TODO: get this from a resource
+ private static final int ICON_GAP = 8;
+ private static final int ICON_WIDTH = 25;
+ private static final int ICON_HEIGHT = 25;
+
+ public View view;
+
+ IconData mData;
+
+ private TextView mTextView;
+ private AnimatedImageView mImageView;
+ private TextView mNumberView;
+
+ public StatusBarIcon(Context context, IconData data, ViewGroup parent) {
+ mData = data.clone();
+
+ switch (data.type) {
+ case IconData.TEXT: {
+ TextView t;
+ t = new TextView(context);
+ mTextView = t;
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.MATCH_PARENT);
+ t.setTextSize(16);
+ t.setTextColor(0xff000000);
+ t.setTypeface(Typeface.DEFAULT_BOLD);
+ t.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);
+ t.setPadding(6, 0, 0, 0);
+ t.setLayoutParams(layoutParams);
+ t.setText(data.text);
+ this.view = t;
+ break;
+ }
+
+ case IconData.ICON: {
+ // container
+ LayoutInflater inflater = (LayoutInflater)context.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflater.inflate(com.android.internal.R.layout.status_bar_icon, parent, false);
+ this.view = v;
+
+ // icon
+ AnimatedImageView im = (AnimatedImageView)v.findViewById(com.android.internal.R.id.image);
+ im.setImageDrawable(getIcon(context, data));
+ im.setImageLevel(data.iconLevel);
+ mImageView = im;
+
+ // number
+ TextView nv = (TextView)v.findViewById(com.android.internal.R.id.number);
+ mNumberView = nv;
+ if (data.number > 0) {
+ nv.setText("" + data.number);
+ nv.setVisibility(View.VISIBLE);
+ } else {
+ nv.setVisibility(View.GONE);
+ }
+ break;
+ }
+ }
+ }
+
+ public void update(Context context, IconData data) throws StatusBarException {
+ if (mData.type != data.type) {
+ throw new StatusBarException("status bar entry type can't change");
+ }
+ switch (data.type) {
+ case IconData.TEXT:
+ if (!TextUtils.equals(mData.text, data.text)) {
+ TextView tv = mTextView;
+ tv.setText(data.text);
+ }
+ break;
+ case IconData.ICON:
+ if (((mData.iconPackage != null && data.iconPackage != null)
+ && !mData.iconPackage.equals(data.iconPackage))
+ || mData.iconId != data.iconId
+ || mData.iconLevel != data.iconLevel) {
+ ImageView im = mImageView;
+ im.setImageDrawable(getIcon(context, data));
+ im.setImageLevel(data.iconLevel);
+ }
+ if (mData.number != data.number) {
+ TextView nv = mNumberView;
+ if (data.number > 0) {
+ nv.setText("" + data.number);
+ } else {
+ nv.setText("");
+ }
+ }
+ break;
+ }
+ mData.copyFrom(data);
+ }
+
+ public void update(int number) {
+ if (mData.number != number) {
+ TextView nv = mNumberView;
+ if (number > 0) {
+ nv.setText("" + number);
+ } else {
+ nv.setText("");
+ }
+ }
+ mData.number = number;
+ }
+
+
+ /**
+ * Returns the right icon to use for this item, respecting the iconId and
+ * iconPackage (if set)
+ *
+ * @param context Context to use to get resources if iconPackage is not set
+ * @return Drawable for this item, or null if the package or item could not
+ * be found
+ */
+ static Drawable getIcon(Context context, IconData data) {
+
+ Resources r = null;
+
+ if (data.iconPackage != null) {
+ try {
+ r = context.getPackageManager().getResourcesForApplication(data.iconPackage);
+ } catch (PackageManager.NameNotFoundException ex) {
+ Slog.e(PhoneStatusBarService.TAG, "Icon package not found: " + data.iconPackage, ex);
+ return null;
+ }
+ } else {
+ r = context.getResources();
+ }
+
+ if (data.iconId == 0) {
+ Slog.w(PhoneStatusBarService.TAG, "No icon ID for slot " + data.slot);
+ return null;
+ }
+
+ try {
+ return r.getDrawable(data.iconId);
+ } catch (RuntimeException e) {
+ Slog.w(PhoneStatusBarService.TAG, "Icon not found in "
+ + (data.iconPackage != null ? data.iconId : "<system>")
+ + ": " + Integer.toHexString(data.iconId));
+ }
+
+ return null;
+ }
+
+ int getNumber() {
+ return mData.number;
+ }
+}
+
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarNotification.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarNotification.java
new file mode 100644
index 0000000..97f77da
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarNotification.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.os.IBinder;
+import android.view.View;
+
+public class StatusBarNotification {
+ IBinder key;
+ NotificationData data;
+ View view;
+ View contentView;
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarPolicy.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarPolicy.java
new file mode 100644
index 0000000..5cc8482
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarPolicy.java
@@ -0,0 +1,1386 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.app.AlertDialog;
+import android.bluetooth.BluetoothA2dp;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothPbap;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.TypedArray;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.location.LocationManager;
+import android.media.AudioManager;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.NetworkInfo;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.storage.StorageManager;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
+import android.text.format.DateFormat;
+import android.text.style.RelativeSizeSpan;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.util.Slog;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.view.WindowManagerImpl;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.telephony.cdma.EriInfo;
+import com.android.internal.telephony.cdma.TtyIntent;
+import com.android.server.am.BatteryStatsService;
+
+import com.android.server.status.IconData;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * This class contains all of the policy about which icons are installed in the status
+ * bar at boot time. In reality, it should go into the android.policy package, but
+ * putting it here is the first step from extracting it.
+ */
+public class StatusBarPolicy {
+ private static final String TAG = "StatusBarPolicy";
+
+ private static StatusBarPolicy sInstance;
+
+ // message codes for the handler
+ private static final int EVENT_BATTERY_CLOSE = 4;
+
+ private final Context mContext;
+ private final StatusBarService mService;
+ private final Handler mHandler = new StatusBarHandler();
+ private final IBatteryStats mBatteryStats;
+
+ // clock
+ private Calendar mCalendar;
+ private String mClockFormatString;
+ private SimpleDateFormat mClockFormat;
+ private IBinder mClockIcon;
+ private IconData mClockData;
+
+ // storage
+ private StorageManager mStorageManager;
+
+ // battery
+ private IBinder mBatteryIcon;
+ private IconData mBatteryData;
+ private boolean mBatteryFirst = true;
+ private boolean mBatteryPlugged;
+ private int mBatteryLevel;
+ private AlertDialog mLowBatteryDialog;
+ private TextView mBatteryLevelTextView;
+ private View mBatteryView;
+ private int mBatteryViewSequence;
+ private boolean mBatteryShowLowOnEndCall = false;
+ private static final boolean SHOW_LOW_BATTERY_WARNING = true;
+ private static final boolean SHOW_BATTERY_WARNINGS_IN_CALL = true;
+
+ // phone
+ private TelephonyManager mPhone;
+ private IBinder mPhoneIcon;
+
+ //***** Signal strength icons
+ private IconData mPhoneData;
+ //GSM/UMTS
+ private static final int[] sSignalImages = new int[] {
+ com.android.internal.R.drawable.stat_sys_signal_0,
+ com.android.internal.R.drawable.stat_sys_signal_1,
+ com.android.internal.R.drawable.stat_sys_signal_2,
+ com.android.internal.R.drawable.stat_sys_signal_3,
+ com.android.internal.R.drawable.stat_sys_signal_4
+ };
+ private static final int[] sSignalImages_r = new int[] {
+ com.android.internal.R.drawable.stat_sys_r_signal_0,
+ com.android.internal.R.drawable.stat_sys_r_signal_1,
+ com.android.internal.R.drawable.stat_sys_r_signal_2,
+ com.android.internal.R.drawable.stat_sys_r_signal_3,
+ com.android.internal.R.drawable.stat_sys_r_signal_4
+ };
+ private static final int[] sRoamingIndicatorImages_cdma = new int[] {
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //Standard Roaming Indicator
+ // 1 is Standard Roaming Indicator OFF
+ // TODO T: image never used, remove and put 0 instead?
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+ // 2 is Standard Roaming Indicator FLASHING
+ // TODO T: image never used, remove and put 0 instead?
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+ // 3-12 Standard ERI
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //3
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+ // 13-63 Reserved for Standard ERI
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //13
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+
+ // 64-127 Reserved for Non Standard (Operator Specific) ERI
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0, //64
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0,
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_0 //83
+
+ // 128-255 Reserved
+ };
+
+ //***** Data connection icons
+ private int[] mDataIconList = sDataNetType_g;
+ //GSM/UMTS
+ private static final int[] sDataNetType_g = new int[] {
+ com.android.internal.R.drawable.stat_sys_data_connected_g,
+ com.android.internal.R.drawable.stat_sys_data_in_g,
+ com.android.internal.R.drawable.stat_sys_data_out_g,
+ com.android.internal.R.drawable.stat_sys_data_inandout_g,
+ };
+ private static final int[] sDataNetType_3g = new int[] {
+ com.android.internal.R.drawable.stat_sys_data_connected_3g,
+ com.android.internal.R.drawable.stat_sys_data_in_3g,
+ com.android.internal.R.drawable.stat_sys_data_out_3g,
+ com.android.internal.R.drawable.stat_sys_data_inandout_3g,
+ };
+ private static final int[] sDataNetType_e = new int[] {
+ com.android.internal.R.drawable.stat_sys_data_connected_e,
+ com.android.internal.R.drawable.stat_sys_data_in_e,
+ com.android.internal.R.drawable.stat_sys_data_out_e,
+ com.android.internal.R.drawable.stat_sys_data_inandout_e,
+ };
+ //3.5G
+ private static final int[] sDataNetType_h = new int[] {
+ com.android.internal.R.drawable.stat_sys_data_connected_h,
+ com.android.internal.R.drawable.stat_sys_data_in_h,
+ com.android.internal.R.drawable.stat_sys_data_out_h,
+ com.android.internal.R.drawable.stat_sys_data_inandout_h,
+ };
+
+ //CDMA
+ // Use 3G icons for EVDO data and 1x icons for 1XRTT data
+ private static final int[] sDataNetType_1x = new int[] {
+ com.android.internal.R.drawable.stat_sys_data_connected_1x,
+ com.android.internal.R.drawable.stat_sys_data_in_1x,
+ com.android.internal.R.drawable.stat_sys_data_out_1x,
+ com.android.internal.R.drawable.stat_sys_data_inandout_1x,
+ };
+
+ // Assume it's all good unless we hear otherwise. We don't always seem
+ // to get broadcasts that it *is* there.
+ IccCard.State mSimState = IccCard.State.READY;
+ int mPhoneState = TelephonyManager.CALL_STATE_IDLE;
+ int mDataState = TelephonyManager.DATA_DISCONNECTED;
+ int mDataActivity = TelephonyManager.DATA_ACTIVITY_NONE;
+ ServiceState mServiceState;
+ SignalStrength mSignalStrength;
+
+ // data connection
+ private IBinder mDataIcon;
+ private IconData mDataData;
+ private boolean mDataIconVisible;
+ private boolean mHspaDataDistinguishable;
+
+ // ringer volume
+ private IBinder mVolumeIcon;
+ private IconData mVolumeData;
+ private boolean mVolumeVisible;
+
+ // bluetooth device status
+ private IBinder mBluetoothIcon;
+ private IconData mBluetoothData;
+ private int mBluetoothHeadsetState;
+ private boolean mBluetoothA2dpConnected;
+ private int mBluetoothPbapState;
+ private boolean mBluetoothEnabled;
+
+ // wifi
+ private static final int[] sWifiSignalImages = new int[] {
+ com.android.internal.R.drawable.stat_sys_wifi_signal_1,
+ com.android.internal.R.drawable.stat_sys_wifi_signal_2,
+ com.android.internal.R.drawable.stat_sys_wifi_signal_3,
+ com.android.internal.R.drawable.stat_sys_wifi_signal_4,
+ };
+ private static final int sWifiTemporarilyNotConnectedImage =
+ com.android.internal.R.drawable.stat_sys_wifi_signal_0;
+
+ private int mLastWifiSignalLevel = -1;
+ private boolean mIsWifiConnected = false;
+ private IBinder mWifiIcon;
+ private IconData mWifiData;
+
+ // gps
+ private IBinder mGpsIcon;
+ private IconData mGpsEnabledIconData;
+ private IconData mGpsFixIconData;
+
+ // alarm clock
+ // Icon lit when clock is set
+ private IBinder mAlarmClockIcon;
+ private IconData mAlarmClockIconData;
+
+ // sync state
+ // If sync is active the SyncActive icon is displayed. If sync is not active but
+ // sync is failing the SyncFailing icon is displayed. Otherwise neither are displayed.
+ private IBinder mSyncActiveIcon;
+ private IBinder mSyncFailingIcon;
+
+ // TTY mode
+ // Icon lit when TTY mode is enabled
+ private IBinder mTTYModeIcon;
+ private IconData mTTYModeEnableIconData;
+
+ // Cdma Roaming Indicator, ERI
+ private IBinder mCdmaRoamingIndicatorIcon;
+ private IconData mCdmaRoamingIndicatorIconData;
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_TIME_TICK)) {
+ updateClock();
+ }
+ else if (action.equals(Intent.ACTION_TIME_CHANGED)) {
+ updateClock();
+ }
+ else if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
+ updateBattery(intent);
+ }
+ else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+ updateClock();
+ }
+ else if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
+ String tz = intent.getStringExtra("time-zone");
+ mCalendar = Calendar.getInstance(TimeZone.getTimeZone(tz));
+ updateClock();
+ }
+ else if (action.equals(Intent.ACTION_ALARM_CHANGED)) {
+ updateAlarm(intent);
+ }
+ else if (action.equals(Intent.ACTION_SYNC_STATE_CHANGED)) {
+ updateSyncState(intent);
+ }
+ else if (action.equals(Intent.ACTION_BATTERY_LOW)) {
+ onBatteryLow(intent);
+ }
+ else if (action.equals(Intent.ACTION_BATTERY_OKAY)
+ || action.equals(Intent.ACTION_POWER_CONNECTED)) {
+ onBatteryOkay(intent);
+ }
+ else if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED) ||
+ action.equals(BluetoothHeadset.ACTION_STATE_CHANGED) ||
+ action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED) ||
+ action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+ updateBluetooth(intent);
+ }
+ else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) ||
+ action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION) ||
+ action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+ updateWifi(intent);
+ }
+ else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) ||
+ action.equals(LocationManager.GPS_FIX_CHANGE_ACTION)) {
+ updateGps(intent);
+ }
+ else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION) ||
+ action.equals(AudioManager.VIBRATE_SETTING_CHANGED_ACTION)) {
+ updateVolume();
+ }
+ else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
+ updateSimState(intent);
+ }
+ else if (action.equals(TtyIntent.TTY_ENABLED_CHANGE_ACTION)) {
+ updateTTY(intent);
+ }
+ }
+ };
+
+ private StatusBarPolicy(Context context, StatusBarService service) {
+ mContext = context;
+ mService = service;
+ mSignalStrength = new SignalStrength();
+ mBatteryStats = BatteryStatsService.getService();
+
+ // clock
+ mCalendar = Calendar.getInstance(TimeZone.getDefault());
+ mClockData = IconData.makeText("clock", "");
+ mClockIcon = service.addIcon(mClockData, null);
+ updateClock();
+
+ // storage
+ mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+ mStorageManager.registerListener(
+ new com.android.server.status.StorageNotification(context));
+
+ // battery
+ mBatteryData = IconData.makeIcon("battery",
+ null, com.android.internal.R.drawable.stat_sys_battery_unknown, 0, 0);
+ mBatteryIcon = service.addIcon(mBatteryData, null);
+
+ // phone_signal
+ mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+ mPhoneData = IconData.makeIcon("phone_signal",
+ null, com.android.internal.R.drawable.stat_sys_signal_null, 0, 0);
+ mPhoneIcon = service.addIcon(mPhoneData, null);
+
+ // register for phone state notifications.
+ ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE))
+ .listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_SERVICE_STATE
+ | PhoneStateListener.LISTEN_SIGNAL_STRENGTHS
+ | PhoneStateListener.LISTEN_CALL_STATE
+ | PhoneStateListener.LISTEN_DATA_CONNECTION_STATE
+ | PhoneStateListener.LISTEN_DATA_ACTIVITY);
+
+ // data_connection
+ mDataData = IconData.makeIcon("data_connection",
+ null, com.android.internal.R.drawable.stat_sys_data_connected_g, 0, 0);
+ mDataIcon = service.addIcon(mDataData, null);
+ service.setIconVisibility(mDataIcon, false);
+
+ // wifi
+ mWifiData = IconData.makeIcon("wifi", null, sWifiSignalImages[0], 0, 0);
+ mWifiIcon = service.addIcon(mWifiData, null);
+ service.setIconVisibility(mWifiIcon, false);
+ // wifi will get updated by the sticky intents
+
+ // TTY status
+ mTTYModeEnableIconData = IconData.makeIcon("tty",
+ null, com.android.internal.R.drawable.stat_sys_tty_mode, 0, 0);
+ mTTYModeIcon = service.addIcon(mTTYModeEnableIconData, null);
+ service.setIconVisibility(mTTYModeIcon, false);
+
+ // Cdma Roaming Indicator, ERI
+ mCdmaRoamingIndicatorIconData = IconData.makeIcon("cdma_eri",
+ null, com.android.internal.R.drawable.stat_sys_roaming_cdma_0, 0, 0);
+ mCdmaRoamingIndicatorIcon = service.addIcon(mCdmaRoamingIndicatorIconData, null);
+ service.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+
+ // bluetooth status
+ mBluetoothData = IconData.makeIcon("bluetooth",
+ null, com.android.internal.R.drawable.stat_sys_data_bluetooth, 0, 0);
+ mBluetoothIcon = service.addIcon(mBluetoothData, null);
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ mBluetoothEnabled = adapter.isEnabled();
+ } else {
+ mBluetoothEnabled = false;
+ }
+ mBluetoothA2dpConnected = false;
+ mBluetoothHeadsetState = BluetoothHeadset.STATE_DISCONNECTED;
+ mBluetoothPbapState = BluetoothPbap.STATE_DISCONNECTED;
+ mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
+
+ // Gps status
+ mGpsEnabledIconData = IconData.makeIcon("gps",
+ null, com.android.internal.R.drawable.stat_sys_gps_acquiring_anim, 0, 0);
+ mGpsFixIconData = IconData.makeIcon("gps",
+ null, com.android.internal.R.drawable.stat_sys_gps_on, 0, 0);
+ mGpsIcon = service.addIcon(mGpsEnabledIconData, null);
+ service.setIconVisibility(mGpsIcon, false);
+
+ // Alarm clock
+ mAlarmClockIconData = IconData.makeIcon(
+ "alarm_clock",
+ null, com.android.internal.R.drawable.stat_notify_alarm, 0, 0);
+ mAlarmClockIcon = service.addIcon(mAlarmClockIconData, null);
+ service.setIconVisibility(mAlarmClockIcon, false);
+
+ // Sync state
+ mSyncActiveIcon = service.addIcon(IconData.makeIcon("sync_active",
+ null, R.drawable.stat_notify_sync_anim0, 0, 0), null);
+ mSyncFailingIcon = service.addIcon(IconData.makeIcon("sync_failing",
+ null, R.drawable.stat_notify_sync_error, 0, 0), null);
+ service.setIconVisibility(mSyncActiveIcon, false);
+ service.setIconVisibility(mSyncFailingIcon, false);
+
+ // volume
+ mVolumeData = IconData.makeIcon("volume",
+ null, com.android.internal.R.drawable.stat_sys_ringer_silent, 0, 0);
+ mVolumeIcon = service.addIcon(mVolumeData, null);
+ service.setIconVisibility(mVolumeIcon, false);
+ updateVolume();
+
+ IntentFilter filter = new IntentFilter();
+
+ // Register for Intent broadcasts for...
+ filter.addAction(Intent.ACTION_TIME_TICK);
+ filter.addAction(Intent.ACTION_TIME_CHANGED);
+ filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.addAction(Intent.ACTION_BATTERY_LOW);
+ filter.addAction(Intent.ACTION_BATTERY_OKAY);
+ filter.addAction(Intent.ACTION_POWER_CONNECTED);
+ filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+ filter.addAction(Intent.ACTION_ALARM_CHANGED);
+ filter.addAction(Intent.ACTION_SYNC_STATE_CHANGED);
+ filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
+ filter.addAction(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
+ filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
+ filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
+ filter.addAction(BluetoothPbap.PBAP_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
+ filter.addAction(LocationManager.GPS_ENABLED_CHANGE_ACTION);
+ filter.addAction(LocationManager.GPS_FIX_CHANGE_ACTION);
+ filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ filter.addAction(TtyIntent.TTY_ENABLED_CHANGE_ACTION);
+ mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+
+ // load config to determine if to distinguish Hspa data icon
+ try {
+ mHspaDataDistinguishable = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_hspa_data_distinguishable);
+ } catch (Exception e) {
+ mHspaDataDistinguishable = false;
+ }
+ }
+
+ public static void installIcons(Context context, StatusBarService service) {
+ sInstance = new StatusBarPolicy(context, service);
+ }
+
+ private final CharSequence getSmallTime() {
+ boolean b24 = DateFormat.is24HourFormat(mContext);
+ int res;
+
+ if (b24) {
+ res = R.string.twenty_four_hour_time_format;
+ } else {
+ res = R.string.twelve_hour_time_format;
+ }
+
+ final char MAGIC1 = '\uEF00';
+ final char MAGIC2 = '\uEF01';
+
+ SimpleDateFormat sdf;
+ String format = mContext.getString(res);
+ if (!format.equals(mClockFormatString)) {
+ /*
+ * Search for an unquoted "a" in the format string, so we can
+ * add dummy characters around it to let us find it again after
+ * formatting and change its size.
+ */
+ int a = -1;
+ boolean quoted = false;
+ for (int i = 0; i < format.length(); i++) {
+ char c = format.charAt(i);
+
+ if (c == '\'') {
+ quoted = !quoted;
+ }
+
+ if (!quoted && c == 'a') {
+ a = i;
+ break;
+ }
+ }
+
+ if (a >= 0) {
+ // Move a back so any whitespace before the AM/PM is also in the alternate size.
+ final int b = a;
+ while (a > 0 && Character.isWhitespace(format.charAt(a-1))) {
+ a--;
+ }
+ format = format.substring(0, a) + MAGIC1 + format.substring(a, b)
+ + "a" + MAGIC2 + format.substring(b + 1);
+ }
+
+ mClockFormat = sdf = new SimpleDateFormat(format);
+ mClockFormatString = format;
+ } else {
+ sdf = mClockFormat;
+ }
+ String result = sdf.format(mCalendar.getTime());
+
+ int magic1 = result.indexOf(MAGIC1);
+ int magic2 = result.indexOf(MAGIC2);
+
+ if (magic1 >= 0 && magic2 > magic1) {
+ SpannableStringBuilder formatted = new SpannableStringBuilder(result);
+
+ formatted.setSpan(new RelativeSizeSpan(0.7f), magic1, magic2,
+ Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
+
+ formatted.delete(magic2, magic2 + 1);
+ formatted.delete(magic1, magic1 + 1);
+
+ return formatted;
+ } else {
+ return result;
+ }
+ }
+
+ private final void updateClock() {
+ mCalendar.setTimeInMillis(System.currentTimeMillis());
+ mClockData.text = getSmallTime();
+ mService.updateIcon(mClockIcon, mClockData, null);
+ }
+
+ private final void updateAlarm(Intent intent) {
+ boolean alarmSet = intent.getBooleanExtra("alarmSet", false);
+ mService.setIconVisibility(mAlarmClockIcon, alarmSet);
+ }
+
+ private final void updateSyncState(Intent intent) {
+ boolean isActive = intent.getBooleanExtra("active", false);
+ boolean isFailing = intent.getBooleanExtra("failing", false);
+ mService.setIconVisibility(mSyncActiveIcon, isActive);
+ // Don't display sync failing icon: BUG 1297963 Set sync error timeout to "never"
+ //mService.setIconVisibility(mSyncFailingIcon, isFailing && !isActive);
+ }
+
+ private final void updateBattery(Intent intent) {
+ mBatteryData.iconId = intent.getIntExtra("icon-small", 0);
+ mBatteryData.iconLevel = intent.getIntExtra("level", 0);
+ mService.updateIcon(mBatteryIcon, mBatteryData, null);
+
+ boolean plugged = intent.getIntExtra("plugged", 0) != 0;
+ int level = intent.getIntExtra("level", -1);
+ if (false) {
+ Slog.d(TAG, "updateBattery level=" + level
+ + " plugged=" + plugged
+ + " mBatteryPlugged=" + mBatteryPlugged
+ + " mBatteryLevel=" + mBatteryLevel
+ + " mBatteryFirst=" + mBatteryFirst);
+ }
+
+ boolean oldPlugged = mBatteryPlugged;
+
+ mBatteryPlugged = plugged;
+ mBatteryLevel = level;
+
+ if (mBatteryFirst) {
+ mBatteryFirst = false;
+ }
+ /*
+ * No longer showing the battery view because it draws attention away
+ * from the USB storage notification. We could still show it when
+ * connected to a brick, but that could lead to the user into thinking
+ * the device does not charge when plugged into USB (since he/she would
+ * not see the same battery screen on USB as he sees on brick).
+ */
+ /* else {
+ if (plugged && !oldPlugged) {
+ showBatteryView();
+ }
+ }
+ */
+ if (false) {
+ Slog.d(TAG, "plugged=" + plugged + " oldPlugged=" + oldPlugged + " level=" + level);
+ }
+ }
+
+ private void onBatteryLow(Intent intent) {
+ if (SHOW_LOW_BATTERY_WARNING) {
+ if (false) {
+ Slog.d(TAG, "mPhoneState=" + mPhoneState
+ + " mLowBatteryDialog=" + mLowBatteryDialog
+ + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
+ }
+
+ if (SHOW_BATTERY_WARNINGS_IN_CALL || mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+ showLowBatteryWarning();
+ } else {
+ mBatteryShowLowOnEndCall = true;
+ }
+ }
+ }
+
+ private void onBatteryOkay(Intent intent) {
+ if (mLowBatteryDialog != null
+ && SHOW_LOW_BATTERY_WARNING) {
+ mLowBatteryDialog.dismiss();
+ mBatteryShowLowOnEndCall = false;
+ }
+ }
+
+ private void showBatteryView() {
+ closeLastBatteryView();
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.dismiss();
+ }
+
+ int level = mBatteryLevel;
+
+ View v = View.inflate(mContext, com.android.internal.R.layout.battery_status, null);
+ mBatteryView = v;
+ int pixelFormat = PixelFormat.TRANSLUCENT;
+ Drawable bg = v.getBackground();
+ if (bg != null) {
+ pixelFormat = bg.getOpacity();
+ }
+
+ int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+
+ if (!mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_sf_slowBlur)) {
+ flags |= WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
+ }
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_TOAST,
+ flags, pixelFormat);
+
+ // Get the dim amount from the theme
+ TypedArray a = mContext.obtainStyledAttributes(
+ com.android.internal.R.styleable.Theme);
+ lp.dimAmount = a.getFloat(android.R.styleable.Theme_backgroundDimAmount, 0.5f);
+ a.recycle();
+
+ lp.setTitle("Battery");
+
+ TextView levelTextView = (TextView)v.findViewById(com.android.internal.R.id.level_percent);
+ levelTextView.setText(mContext.getString(
+ com.android.internal.R.string.battery_status_text_percent_format, level));
+
+ setBatteryLevel(v, com.android.internal.R.id.spacer, 100-level, 0, 0);
+ setBatteryLevel(v, com.android.internal.R.id.level, level,
+ com.android.internal.R.drawable.battery_charge_fill, level);
+
+ WindowManagerImpl.getDefault().addView(v, lp);
+
+ scheduleCloseBatteryView();
+ }
+
+ private void setBatteryLevel(View parent, int id, int height, int background, int level) {
+ ImageView v = (ImageView)parent.findViewById(id);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)v.getLayoutParams();
+ lp.weight = height;
+ if (background != 0) {
+ v.setBackgroundResource(background);
+ Drawable bkg = v.getBackground();
+ bkg.setLevel(level);
+ }
+ }
+
+ private void showLowBatteryWarning() {
+ closeLastBatteryView();
+
+ // Show exact battery level.
+ CharSequence levelText = mContext.getString(
+ com.android.internal.R.string.battery_low_percent_format, mBatteryLevel);
+
+ if (mBatteryLevelTextView != null) {
+ mBatteryLevelTextView.setText(levelText);
+ } else {
+ View v = View.inflate(mContext, com.android.internal.R.layout.battery_low, null);
+ mBatteryLevelTextView=(TextView)v.findViewById(com.android.internal.R.id.level_percent);
+
+ mBatteryLevelTextView.setText(levelText);
+
+ AlertDialog.Builder b = new AlertDialog.Builder(mContext);
+ b.setCancelable(true);
+ b.setTitle(com.android.internal.R.string.battery_low_title);
+ b.setView(v);
+ b.setIcon(android.R.drawable.ic_dialog_alert);
+ b.setPositiveButton(android.R.string.ok, null);
+
+ final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NO_HISTORY);
+ if (intent.resolveActivity(mContext.getPackageManager()) != null) {
+ b.setNegativeButton(com.android.internal.R.string.battery_low_why,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ mContext.startActivity(intent);
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.dismiss();
+ }
+ }
+ });
+ }
+
+ AlertDialog d = b.create();
+ d.setOnDismissListener(mLowBatteryListener);
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ d.show();
+ mLowBatteryDialog = d;
+ }
+
+ final ContentResolver cr = mContext.getContentResolver();
+ if (Settings.System.getInt(cr,
+ Settings.System.POWER_SOUNDS_ENABLED, 1) == 1)
+ {
+ final String soundPath = Settings.System.getString(cr,
+ Settings.System.LOW_BATTERY_SOUND);
+ if (soundPath != null) {
+ final Uri soundUri = Uri.parse("file://" + soundPath);
+ if (soundUri != null) {
+ final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
+ if (sfx != null) {
+ sfx.setStreamType(AudioManager.STREAM_SYSTEM);
+ sfx.play();
+ }
+ }
+ }
+ }
+ }
+
+ private final void updateCallState(int state) {
+ mPhoneState = state;
+ if (false) {
+ Slog.d(TAG, "mPhoneState=" + mPhoneState
+ + " mLowBatteryDialog=" + mLowBatteryDialog
+ + " mBatteryShowLowOnEndCall=" + mBatteryShowLowOnEndCall);
+ }
+ if (mPhoneState == TelephonyManager.CALL_STATE_IDLE) {
+ if (mBatteryShowLowOnEndCall) {
+ if (!mBatteryPlugged) {
+ showLowBatteryWarning();
+ }
+ mBatteryShowLowOnEndCall = false;
+ }
+ } else {
+ if (mLowBatteryDialog != null) {
+ mLowBatteryDialog.dismiss();
+ mBatteryShowLowOnEndCall = true;
+ }
+ }
+ }
+
+ private DialogInterface.OnDismissListener mLowBatteryListener
+ = new DialogInterface.OnDismissListener() {
+ public void onDismiss(DialogInterface dialog) {
+ mLowBatteryDialog = null;
+ mBatteryLevelTextView = null;
+ }
+ };
+
+ private void scheduleCloseBatteryView() {
+ Message m = mHandler.obtainMessage(EVENT_BATTERY_CLOSE);
+ m.arg1 = (++mBatteryViewSequence);
+ mHandler.sendMessageDelayed(m, 3000);
+ }
+
+ private void closeLastBatteryView() {
+ if (mBatteryView != null) {
+ //mBatteryView.debug();
+ WindowManagerImpl.getDefault().removeView(mBatteryView);
+ mBatteryView = null;
+ }
+ }
+
+ private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onSignalStrengthsChanged(SignalStrength signalStrength) {
+ mSignalStrength = signalStrength;
+ updateSignalStrength();
+ }
+
+ @Override
+ public void onServiceStateChanged(ServiceState state) {
+ mServiceState = state;
+ updateSignalStrength();
+ updateCdmaRoamingIcon(state);
+ updateDataIcon();
+ }
+
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ updateCallState(state);
+ // In cdma, if a voice call is made, RSSI should switch to 1x.
+ if (isCdma()) {
+ updateSignalStrength();
+ }
+ }
+
+ @Override
+ public void onDataConnectionStateChanged(int state, int networkType) {
+ mDataState = state;
+ updateDataNetType(networkType);
+ updateDataIcon();
+ }
+
+ @Override
+ public void onDataActivity(int direction) {
+ mDataActivity = direction;
+ updateDataIcon();
+ }
+ };
+
+ private final void updateSimState(Intent intent) {
+ String stateExtra = intent.getStringExtra(IccCard.INTENT_KEY_ICC_STATE);
+ if (IccCard.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) {
+ mSimState = IccCard.State.ABSENT;
+ }
+ else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) {
+ mSimState = IccCard.State.READY;
+ }
+ else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) {
+ final String lockedReason = intent.getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON);
+ if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) {
+ mSimState = IccCard.State.PIN_REQUIRED;
+ }
+ else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) {
+ mSimState = IccCard.State.PUK_REQUIRED;
+ }
+ else {
+ mSimState = IccCard.State.NETWORK_LOCKED;
+ }
+ } else {
+ mSimState = IccCard.State.UNKNOWN;
+ }
+ updateDataIcon();
+ }
+
+ private boolean isCdma() {
+ return (mSignalStrength != null) && !mSignalStrength.isGsm();
+ }
+
+ private boolean isEvdo() {
+ return ( (mServiceState != null)
+ && ((mServiceState.getRadioTechnology()
+ == ServiceState.RADIO_TECHNOLOGY_EVDO_0)
+ || (mServiceState.getRadioTechnology()
+ == ServiceState.RADIO_TECHNOLOGY_EVDO_A)));
+ }
+
+ private boolean hasService() {
+ if (mServiceState != null) {
+ switch (mServiceState.getState()) {
+ case ServiceState.STATE_OUT_OF_SERVICE:
+ case ServiceState.STATE_POWER_OFF:
+ return false;
+ default:
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ private final void updateSignalStrength() {
+ int iconLevel = -1;
+ int[] iconList;
+
+ if (!hasService()) {
+ //Slog.d(TAG, "updateSignalStrength: no service");
+ if (Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
+ mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_flightmode;
+ } else {
+ mPhoneData.iconId = com.android.internal.R.drawable.stat_sys_signal_null;
+ }
+ mService.updateIcon(mPhoneIcon, mPhoneData, null);
+ return;
+ }
+
+ if (!isCdma()) {
+ int asu = mSignalStrength.getGsmSignalStrength();
+
+ // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
+ // asu = 0 (-113dB or less) is very weak
+ // signal, its better to show 0 bars to the user in such cases.
+ // asu = 99 is a special case, where the signal strength is unknown.
+ if (asu <= 2 || asu == 99) iconLevel = 0;
+ else if (asu >= 12) iconLevel = 4;
+ else if (asu >= 8) iconLevel = 3;
+ else if (asu >= 5) iconLevel = 2;
+ else iconLevel = 1;
+
+ // Though mPhone is a Manager, this call is not an IPC
+ if (mPhone.isNetworkRoaming()) {
+ iconList = sSignalImages_r;
+ } else {
+ iconList = sSignalImages;
+ }
+ } else {
+ iconList = this.sSignalImages;
+
+ // If 3G(EV) and 1x network are available than 3G should be
+ // displayed, displayed RSSI should be from the EV side.
+ // If a voice call is made then RSSI should switch to 1x.
+ if ((mPhoneState == TelephonyManager.CALL_STATE_IDLE) && isEvdo()){
+ iconLevel = getEvdoLevel();
+ if (false) {
+ Slog.d(TAG, "use Evdo level=" + iconLevel + " to replace Cdma Level=" + getCdmaLevel());
+ }
+ } else {
+ iconLevel = getCdmaLevel();
+ }
+ }
+ mPhoneData.iconId = iconList[iconLevel];
+ mService.updateIcon(mPhoneIcon, mPhoneData, null);
+ }
+
+ private int getCdmaLevel() {
+ final int cdmaDbm = mSignalStrength.getCdmaDbm();
+ final int cdmaEcio = mSignalStrength.getCdmaEcio();
+ int levelDbm = 0;
+ int levelEcio = 0;
+
+ if (cdmaDbm >= -75) levelDbm = 4;
+ else if (cdmaDbm >= -85) levelDbm = 3;
+ else if (cdmaDbm >= -95) levelDbm = 2;
+ else if (cdmaDbm >= -100) levelDbm = 1;
+ else levelDbm = 0;
+
+ // Ec/Io are in dB*10
+ if (cdmaEcio >= -90) levelEcio = 4;
+ else if (cdmaEcio >= -110) levelEcio = 3;
+ else if (cdmaEcio >= -130) levelEcio = 2;
+ else if (cdmaEcio >= -150) levelEcio = 1;
+ else levelEcio = 0;
+
+ return (levelDbm < levelEcio) ? levelDbm : levelEcio;
+ }
+
+ private int getEvdoLevel() {
+ int evdoDbm = mSignalStrength.getEvdoDbm();
+ int evdoSnr = mSignalStrength.getEvdoSnr();
+ int levelEvdoDbm = 0;
+ int levelEvdoSnr = 0;
+
+ if (evdoDbm >= -65) levelEvdoDbm = 4;
+ else if (evdoDbm >= -75) levelEvdoDbm = 3;
+ else if (evdoDbm >= -90) levelEvdoDbm = 2;
+ else if (evdoDbm >= -105) levelEvdoDbm = 1;
+ else levelEvdoDbm = 0;
+
+ if (evdoSnr >= 7) levelEvdoSnr = 4;
+ else if (evdoSnr >= 5) levelEvdoSnr = 3;
+ else if (evdoSnr >= 3) levelEvdoSnr = 2;
+ else if (evdoSnr >= 1) levelEvdoSnr = 1;
+ else levelEvdoSnr = 0;
+
+ return (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
+ }
+
+ private final void updateDataNetType(int net) {
+
+ switch (net) {
+ case TelephonyManager.NETWORK_TYPE_EDGE:
+ mDataIconList = sDataNetType_e;
+ break;
+ case TelephonyManager.NETWORK_TYPE_UMTS:
+ mDataIconList = sDataNetType_3g;
+ break;
+ case TelephonyManager.NETWORK_TYPE_HSDPA:
+ case TelephonyManager.NETWORK_TYPE_HSUPA:
+ case TelephonyManager.NETWORK_TYPE_HSPA:
+ if (mHspaDataDistinguishable) {
+ mDataIconList = sDataNetType_h;
+ } else {
+ mDataIconList = sDataNetType_3g;
+ }
+ break;
+ case TelephonyManager.NETWORK_TYPE_CDMA:
+ // display 1xRTT for IS95A/B
+ mDataIconList = this.sDataNetType_1x;
+ break;
+ case TelephonyManager.NETWORK_TYPE_1xRTT:
+ mDataIconList = this.sDataNetType_1x;
+ break;
+ case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through
+ case TelephonyManager.NETWORK_TYPE_EVDO_A:
+ mDataIconList = sDataNetType_3g;
+ break;
+ default:
+ mDataIconList = sDataNetType_g;
+ break;
+ }
+ }
+
+ private final void updateDataIcon() {
+ int iconId;
+ boolean visible = true;
+
+ if (!isCdma()) {
+ // GSM case, we have to check also the sim state
+ if (mSimState == IccCard.State.READY || mSimState == IccCard.State.UNKNOWN) {
+ if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+ switch (mDataActivity) {
+ case TelephonyManager.DATA_ACTIVITY_IN:
+ iconId = mDataIconList[1];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_OUT:
+ iconId = mDataIconList[2];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_INOUT:
+ iconId = mDataIconList[3];
+ break;
+ default:
+ iconId = mDataIconList[0];
+ break;
+ }
+ mDataData.iconId = iconId;
+ mService.updateIcon(mDataIcon, mDataData, null);
+ } else {
+ visible = false;
+ }
+ } else {
+ mDataData.iconId = com.android.internal.R.drawable.stat_sys_no_sim;
+ mService.updateIcon(mDataIcon, mDataData, null);
+ }
+ } else {
+ // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT
+ if (hasService() && mDataState == TelephonyManager.DATA_CONNECTED) {
+ switch (mDataActivity) {
+ case TelephonyManager.DATA_ACTIVITY_IN:
+ iconId = mDataIconList[1];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_OUT:
+ iconId = mDataIconList[2];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_INOUT:
+ iconId = mDataIconList[3];
+ break;
+ case TelephonyManager.DATA_ACTIVITY_DORMANT:
+ default:
+ iconId = mDataIconList[0];
+ break;
+ }
+ mDataData.iconId = iconId;
+ mService.updateIcon(mDataIcon, mDataData, null);
+ } else {
+ visible = false;
+ }
+ }
+
+ long ident = Binder.clearCallingIdentity();
+ try {
+ mBatteryStats.notePhoneDataConnectionState(mPhone.getNetworkType(), visible);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ if (mDataIconVisible != visible) {
+ mService.setIconVisibility(mDataIcon, visible);
+ mDataIconVisible = visible;
+ }
+ }
+
+ private final void updateVolume() {
+ AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ final int ringerMode = audioManager.getRingerMode();
+ final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT ||
+ ringerMode == AudioManager.RINGER_MODE_VIBRATE;
+ final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)
+ ? com.android.internal.R.drawable.stat_sys_ringer_vibrate
+ : com.android.internal.R.drawable.stat_sys_ringer_silent;
+
+ if (visible) {
+ mVolumeData.iconId = iconId;
+ mService.updateIcon(mVolumeIcon, mVolumeData, null);
+ }
+ if (visible != mVolumeVisible) {
+ mService.setIconVisibility(mVolumeIcon, visible);
+ mVolumeVisible = visible;
+ }
+ }
+
+ private final void updateBluetooth(Intent intent) {
+ int iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth;
+ String action = intent.getAction();
+ if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
+ mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
+ } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
+ mBluetoothHeadsetState = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
+ BluetoothHeadset.STATE_ERROR);
+ } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
+ BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
+ if (a2dp.getConnectedSinks().size() != 0) {
+ mBluetoothA2dpConnected = true;
+ } else {
+ mBluetoothA2dpConnected = false;
+ }
+ } else if (action.equals(BluetoothPbap.PBAP_STATE_CHANGED_ACTION)) {
+ mBluetoothPbapState = intent.getIntExtra(BluetoothPbap.PBAP_STATE,
+ BluetoothPbap.STATE_DISCONNECTED);
+ } else {
+ return;
+ }
+
+ if (mBluetoothHeadsetState == BluetoothHeadset.STATE_CONNECTED || mBluetoothA2dpConnected ||
+ mBluetoothPbapState == BluetoothPbap.STATE_CONNECTED) {
+ iconId = com.android.internal.R.drawable.stat_sys_data_bluetooth_connected;
+ }
+
+ mBluetoothData.iconId = iconId;
+ mService.updateIcon(mBluetoothIcon, mBluetoothData, null);
+ mService.setIconVisibility(mBluetoothIcon, mBluetoothEnabled);
+ }
+
+ private final void updateWifi(Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+
+ final boolean enabled = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED;
+
+ if (!enabled) {
+ // If disabled, hide the icon. (We show icon when connected.)
+ mService.setIconVisibility(mWifiIcon, false);
+ }
+
+ } else if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
+ final boolean enabled = intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED,
+ false);
+ if (!enabled) {
+ mService.setIconVisibility(mWifiIcon, false);
+ }
+ } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+
+ final NetworkInfo networkInfo = (NetworkInfo)
+ intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+
+ int iconId;
+ if (networkInfo != null && networkInfo.isConnected()) {
+ mIsWifiConnected = true;
+ if (mLastWifiSignalLevel == -1) {
+ iconId = sWifiSignalImages[0];
+ } else {
+ iconId = sWifiSignalImages[mLastWifiSignalLevel];
+ }
+
+ // Show the icon since wi-fi is connected
+ mService.setIconVisibility(mWifiIcon, true);
+
+ } else {
+ mLastWifiSignalLevel = -1;
+ mIsWifiConnected = false;
+ iconId = sWifiSignalImages[0];
+
+ // Hide the icon since we're not connected
+ mService.setIconVisibility(mWifiIcon, false);
+ }
+
+ mWifiData.iconId = iconId;
+ mService.updateIcon(mWifiIcon, mWifiData, null);
+ } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
+ final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200);
+ int newSignalLevel = WifiManager.calculateSignalLevel(newRssi,
+ sWifiSignalImages.length);
+ if (newSignalLevel != mLastWifiSignalLevel) {
+ mLastWifiSignalLevel = newSignalLevel;
+ if (mIsWifiConnected) {
+ mWifiData.iconId = sWifiSignalImages[newSignalLevel];
+ } else {
+ mWifiData.iconId = sWifiTemporarilyNotConnectedImage;
+ }
+ mService.updateIcon(mWifiIcon, mWifiData, null);
+ }
+ }
+ }
+
+ private final void updateGps(Intent intent) {
+ final String action = intent.getAction();
+ final boolean enabled = intent.getBooleanExtra(LocationManager.EXTRA_GPS_ENABLED, false);
+
+ if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) {
+ // GPS is getting fixes
+ mService.updateIcon(mGpsIcon, mGpsFixIconData, null);
+ mService.setIconVisibility(mGpsIcon, true);
+ } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) {
+ // GPS is off
+ mService.setIconVisibility(mGpsIcon, false);
+ } else {
+ // GPS is on, but not receiving fixes
+ mService.updateIcon(mGpsIcon, mGpsEnabledIconData, null);
+ mService.setIconVisibility(mGpsIcon, true);
+ }
+ }
+
+ private final void updateTTY(Intent intent) {
+ final String action = intent.getAction();
+ final boolean enabled = intent.getBooleanExtra(TtyIntent.TTY_ENABLED, false);
+
+ if (false) Slog.v(TAG, "updateTTY: enabled: " + enabled);
+
+ if (enabled) {
+ // TTY is on
+ if (false) Slog.v(TAG, "updateTTY: set TTY on");
+ mService.updateIcon(mTTYModeIcon, mTTYModeEnableIconData, null);
+ mService.setIconVisibility(mTTYModeIcon, true);
+ } else {
+ // TTY is off
+ if (false) Slog.v(TAG, "updateTTY: set TTY off");
+ mService.setIconVisibility(mTTYModeIcon, false);
+ }
+ }
+
+ private final void updateCdmaRoamingIcon(ServiceState state) {
+ if (!hasService()) {
+ mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+ return;
+ }
+
+ if (!isCdma()) {
+ mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+ return;
+ }
+
+ int[] iconList = sRoamingIndicatorImages_cdma;
+ int iconIndex = state.getCdmaEriIconIndex();
+ int iconMode = state.getCdmaEriIconMode();
+
+ if (iconIndex == -1) {
+ Slog.e(TAG, "getCdmaEriIconIndex returned null, skipping ERI icon update");
+ return;
+ }
+
+ if (iconMode == -1) {
+ Slog.e(TAG, "getCdmeEriIconMode returned null, skipping ERI icon update");
+ return;
+ }
+
+ if (iconIndex == EriInfo.ROAMING_INDICATOR_OFF) {
+ if (false) Slog.v(TAG, "Cdma ROAMING_INDICATOR_OFF, removing ERI icon");
+ mService.setIconVisibility(mCdmaRoamingIndicatorIcon, false);
+ return;
+ }
+
+ switch (iconMode) {
+ case EriInfo.ROAMING_ICON_MODE_NORMAL:
+ mCdmaRoamingIndicatorIconData.iconId = iconList[iconIndex];
+ mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
+ mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
+ break;
+ case EriInfo.ROAMING_ICON_MODE_FLASH:
+ mCdmaRoamingIndicatorIconData.iconId =
+ com.android.internal.R.drawable.stat_sys_roaming_cdma_flash;
+ mService.updateIcon(mCdmaRoamingIndicatorIcon, mCdmaRoamingIndicatorIconData, null);
+ mService.setIconVisibility(mCdmaRoamingIndicatorIcon, true);
+ break;
+
+ }
+ mService.updateIcon(mPhoneIcon, mPhoneData, null);
+ }
+
+
+ private class StatusBarHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_BATTERY_CLOSE:
+ if (msg.arg1 == mBatteryViewSequence) {
+ closeLastBatteryView();
+ }
+ break;
+ }
+ }
+ }
+}
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java
index de80fc0..3fe71d8 100644
--- a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarService.java
@@ -34,12 +34,22 @@
import android.view.WindowManager;
import android.view.WindowManagerImpl;
+import com.android.server.status.IconData;
+import com.android.server.status.NotificationData;
+
public abstract class StatusBarService extends Service {
private static final String TAG = "StatusBarService";
Bar mBar = new Bar();
IStatusBarService mBarService;
+ /* TODO
+ H mHandler = new H();
+ Object mQueueLock = new Object();
+ ArrayList<PendingOp> mQueue = new ArrayList<PendingOp>();
+ NotificationCallbacks mNotificationCallbacks;
+ */
+
@Override
public void onCreate() {
// Put up the view
@@ -75,5 +85,28 @@
* Implement this to add the main status bar view.
*/
protected abstract void addStatusBarView();
+
+ public void activate() {
+ }
+
+ public void deactivate() {
+ }
+
+ public void toggle() {
+ }
+
+ public void disable(int what, IBinder token, String pkg) {
+ }
+
+ public IBinder addIcon(IconData data, NotificationData n) {
+ return null;
+ }
+
+ public void updateIcon(IBinder key, IconData data, NotificationData n) {
+ }
+
+ public void setIconVisibility(IBinder key, boolean visible) {
+ //addPendingOp(OP_SET_VISIBLE, key, visible);
+ }
}
diff --git a/services/java/com/android/server/status/StatusBarView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarView.java
similarity index 96%
rename from services/java/com/android/server/status/StatusBarView.java
rename to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarView.java
index 5743854..ae703bf 100644
--- a/services/java/com/android/server/status/StatusBarView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/StatusBarView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.policy.statusbar.phone;
import android.content.Context;
import android.content.res.Configuration;
@@ -27,14 +27,12 @@
import android.view.ViewParent;
import android.widget.FrameLayout;
-import com.android.internal.R;
-
public class StatusBarView extends FrameLayout {
private static final String TAG = "StatusBarView";
static final int DIM_ANIM_TIME = 400;
- StatusBarManagerService mService;
+ PhoneStatusBarService mService;
boolean mTracking;
int mStartX, mStartY;
ViewGroup mNotificationIcons;
@@ -94,7 +92,7 @@
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- mService.updateExpandedViewPos(StatusBarManagerService.EXPANDED_LEAVE_ALONE);
+ mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE);
}
@Override
diff --git a/services/java/com/android/server/status/Ticker.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Ticker.java
similarity index 98%
rename from services/java/com/android/server/status/Ticker.java
rename to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Ticker.java
index e47185b..ee82948 100644
--- a/services/java/com/android/server/status/Ticker.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/Ticker.java
@@ -14,9 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
-
-import com.android.internal.R;
+package com.android.policy.statusbar.phone;
import android.content.Context;
import android.graphics.drawable.Drawable;
@@ -36,7 +34,7 @@
import java.util.ArrayList;
-abstract class Ticker {
+public abstract class Ticker {
private static final int TICKER_SEGMENT_DELAY = 3000;
private final class Segment {
diff --git a/services/java/com/android/server/status/TickerView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TickerView.java
similarity index 95%
rename from services/java/com/android/server/status/TickerView.java
rename to packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TickerView.java
index 099dffb..2b98fc5 100644
--- a/services/java/com/android/server/status/TickerView.java
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TickerView.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.status;
+package com.android.policy.statusbar.phone;
import android.content.Context;
import android.util.AttributeSet;
diff --git a/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TrackingView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TrackingView.java
new file mode 100644
index 0000000..9b813e6
--- /dev/null
+++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/TrackingView.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 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.policy.statusbar.phone;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.widget.LinearLayout;
+
+
+public class TrackingView extends LinearLayout {
+ final Display mDisplay;
+ PhoneStatusBarService mService;
+ boolean mTracking;
+ int mStartX, mStartY;
+
+ public TrackingView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mDisplay = ((WindowManager)context.getSystemService(
+ Context.WINDOW_SERVICE)).getDefaultDisplay();
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ super.onLayout(changed, left, top, right, bottom);
+ mService.updateExpandedHeight();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ switch (event.getKeyCode()) {
+ case KeyEvent.KEYCODE_BACK:
+ if (down) {
+ mService.deactivate();
+ }
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mService.onTrackingViewAttached();
+ }
+}
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
index a24e239..e18e9dd 100644
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ b/services/java/com/android/server/status/NotificationViewList.java
@@ -21,11 +21,11 @@
import android.view.View;
import java.util.ArrayList;
-class NotificationViewList {
+public class NotificationViewList {
private ArrayList<StatusBarNotification> mOngoing = new ArrayList();
private ArrayList<StatusBarNotification> mLatest = new ArrayList();
- NotificationViewList() {
+ public NotificationViewList() {
}
private static final int indexInList(ArrayList<StatusBarNotification> list, NotificationData n){
diff --git a/services/java/com/android/server/status/StatusBarIcon.java b/services/java/com/android/server/status/StatusBarIcon.java
index dfccf94..5849ef9 100644
--- a/services/java/com/android/server/status/StatusBarIcon.java
+++ b/services/java/com/android/server/status/StatusBarIcon.java
@@ -31,7 +31,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;
-class StatusBarIcon {
+public class StatusBarIcon {
// TODO: get this from a resource
private static final int ICON_GAP = 8;
private static final int ICON_WIDTH = 25;
diff --git a/services/java/com/android/server/status/StatusBarManagerService.java b/services/java/com/android/server/status/StatusBarManagerService.java
index 18cb1ed..b48a575 100644
--- a/services/java/com/android/server/status/StatusBarManagerService.java
+++ b/services/java/com/android/server/status/StatusBarManagerService.java
@@ -154,7 +154,7 @@
final Context mContext;
final Display mDisplay;
- StatusBarView mStatusBarView;
+ View /*StatusBarView*/ mStatusBarView;
int mPixelFormat;
H mHandler = new H();
Object mQueueLock = new Object();
@@ -207,8 +207,6 @@
int mTrackingPosition; // the position of the top of the tracking view.
// ticker
- private Ticker mTicker;
- private View mTickerView;
private boolean mTicking;
// Tracking finger for opening/closing.
@@ -240,7 +238,6 @@
mContext = context;
mDisplay = ((WindowManager)context.getSystemService(
Context.WINDOW_SERVICE)).getDefaultDisplay();
- makeStatusBarView(context);
mUninstallReceiver = new UninstallReceiver();
}
@@ -251,106 +248,8 @@
// ================================================================================
// Constructing the view
// ================================================================================
- private void makeStatusBarView(Context context) {
- Resources res = context.getResources();
- mRightIconSlots = res.getStringArray(com.android.internal.R.array.status_bar_icon_order);
- mRightIcons = new StatusBarIcon[mRightIconSlots.length];
-
- ExpandedView expanded = (ExpandedView)View.inflate(context,
- com.android.internal.R.layout.status_bar_expanded, null);
- expanded.mService = this;
- StatusBarView sb = (StatusBarView)View.inflate(context,
- com.android.internal.R.layout.status_bar, null);
- sb.mService = this;
-
- // figure out which pixel-format to use for the status bar.
- mPixelFormat = PixelFormat.TRANSLUCENT;
- Drawable bg = sb.getBackground();
- if (bg != null) {
- mPixelFormat = bg.getOpacity();
- }
-
- mStatusBarView = sb;
- mStatusIcons = (LinearLayout)sb.findViewById(R.id.statusIcons);
- mNotificationIcons = (IconMerger)sb.findViewById(R.id.notificationIcons);
- mNotificationIcons.service = this;
- mIcons = (LinearLayout)sb.findViewById(R.id.icons);
- mTickerView = sb.findViewById(R.id.ticker);
- mDateView = (DateView)sb.findViewById(R.id.date);
-
- mExpandedDialog = new ExpandedDialog(context);
- mExpandedView = expanded;
- mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
- mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
- mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
- mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
- mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
- mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
- mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
- mSpnLabel = (TextView)expanded.findViewById(R.id.spnLabel);
- mPlmnLabel = (TextView)expanded.findViewById(R.id.plmnLabel);
- mScrollView = (ScrollView)expanded.findViewById(R.id.scroll);
- mNotificationLinearLayout = expanded.findViewById(R.id.notificationLinearLayout);
-
- mOngoingTitle.setVisibility(View.GONE);
- mLatestTitle.setVisibility(View.GONE);
-
- mTicker = new MyTicker(context, sb);
-
- TickerView tickerView = (TickerView)sb.findViewById(R.id.tickerText);
- tickerView.mTicker = mTicker;
-
- mTrackingView = (TrackingView)View.inflate(context,
- com.android.internal.R.layout.status_bar_tracking, null);
- mTrackingView.mService = this;
- mCloseView = (CloseDragHandle)mTrackingView.findViewById(R.id.close);
- mCloseView.mService = this;
-
- mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore);
-
- // add the more icon for the notifications
- IconData moreData = IconData.makeIcon(null, context.getPackageName(),
- R.drawable.stat_notify_more, 0, 42);
- mMoreIcon = new StatusBarIcon(context, moreData, mNotificationIcons);
- mMoreIcon.view.setId(R.drawable.stat_notify_more);
- mNotificationIcons.moreIcon = mMoreIcon;
- mNotificationIcons.addView(mMoreIcon.view);
-
- // set the inital view visibility
- setAreThereNotifications();
- mDateView.setVisibility(View.INVISIBLE);
-
- // before we register for broadcasts
- mPlmnLabel.setText(R.string.lockscreen_carrier_default);
- mPlmnLabel.setVisibility(View.VISIBLE);
- mSpnLabel.setText("");
- mSpnLabel.setVisibility(View.GONE);
-
- // receive broadcasts
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
- filter.addAction(Intent.ACTION_SCREEN_OFF);
- filter.addAction(Telephony.Intents.SPN_STRINGS_UPDATED_ACTION);
- context.registerReceiver(mBroadcastReceiver, filter);
- }
public void systemReady() {
- final StatusBarView view = mStatusBarView;
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.MATCH_PARENT,
- view.getContext().getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.status_bar_height),
- WindowManager.LayoutParams.TYPE_STATUS_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE|
- WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING,
- mPixelFormat);
- lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL;
- lp.setTitle("StatusBar");
- lp.windowAnimations = R.style.Animation_StatusBar;
-
- //WindowManagerImpl.getDefault().addView(view, lp);
}
public void systemReady2() {
@@ -431,6 +330,10 @@
// Can be called from any thread
// ================================================================================
public IBinder addIcon(IconData data, NotificationData n) {
+ if (true) {
+ return new Binder();
+ }
+ // TODO: Call onto the IStatusBar
int slot;
// assert early-on if they using a slot that doesn't exist.
if (data != null && n == null) {
@@ -681,6 +584,9 @@
/* private */ void performAddUpdateIcon(IBinder key, IconData data, NotificationData n)
throws StatusBarException {
+ if (true) {
+ return;
+ }
if (SPEW) {
Slog.d(TAG, "performAddUpdateIcon icon=" + data + " notification=" + n + " key=" + key);
}
@@ -715,7 +621,7 @@
|| !CharSequences.equals(oldData.tickerText, n.tickerText))) {
if (0 == (mDisabled &
(StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
- mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
+ //mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
}
}
updateExpandedViewPos(EXPANDED_LEAVE_ALONE);
@@ -773,6 +679,9 @@
}
/* private */ void performSetIconVisibility(IBinder key, boolean visible) {
+ if (true) {
+ return;
+ }
synchronized (mIconMap) {
if (SPEW) {
Slog.d(TAG, "performSetIconVisibility key=" + key + " visible=" + visible);
@@ -1340,47 +1249,6 @@
}
}
- private class MyTicker extends Ticker {
- MyTicker(Context context, StatusBarView sb) {
- super(context, sb);
- }
-
- @Override
- void tickerStarting() {
- mTicking = true;
- mIcons.setVisibility(View.GONE);
- mTickerView.setVisibility(View.VISIBLE);
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_up_in, null));
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_up_out, null));
- if (mExpandedVisible) {
- setDateViewVisibility(false, com.android.internal.R.anim.push_up_out);
- }
- }
-
- @Override
- void tickerDone() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.push_down_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.push_down_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.push_down_in);
- }
- }
-
- void tickerHalting() {
- mIcons.setVisibility(View.VISIBLE);
- mTickerView.setVisibility(View.GONE);
- mIcons.startAnimation(loadAnim(com.android.internal.R.anim.fade_in, null));
- mTickerView.startAnimation(loadAnim(com.android.internal.R.anim.fade_out,
- mTickingDoneListener));
- if (mExpandedVisible) {
- setDateViewVisibility(true, com.android.internal.R.anim.fade_in);
- }
- }
- }
-
Animation.AnimationListener mTickingDoneListener = new Animation.AnimationListener() {;
public void onAnimationEnd(Animation animation) {
mTicking = false;
@@ -1448,7 +1316,6 @@
pw.println(" mLatestItems: " + viewInfo(mLatestItems));
pw.println(" mNoNotificationsTitle: " + viewInfo(mNoNotificationsTitle));
pw.println(" mCloseView: " + viewInfo(mCloseView));
- pw.println(" mTickerView: " + viewInfo(mTickerView));
pw.println(" mScrollView: " + viewInfo(mScrollView)
+ " scroll " + mScrollView.getScrollX() + "," + mScrollView.getScrollY());
pw.println("mNotificationLinearLayout: " + viewInfo(mNotificationLinearLayout));
@@ -1591,12 +1458,14 @@
}
void setNotificationIconVisibility(boolean visible, int anim) {
+ /*
int old = mNotificationIcons.getVisibility();
int v = visible ? View.VISIBLE : View.INVISIBLE;
if (old != v) {
mNotificationIcons.setVisibility(v);
mNotificationIcons.startAnimation(loadAnim(anim, null));
}
+ */
}
void updateExpandedViewPos(int expandedPosition) {
@@ -1729,7 +1598,7 @@
Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes");
if (mTicking) {
mNotificationIcons.setVisibility(View.INVISIBLE);
- mTicker.halt();
+ //mTicker.halt();
} else {
setNotificationIconVisibility(false, com.android.internal.R.anim.fade_out);
}
@@ -1741,7 +1610,7 @@
}
} else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- mTicker.halt();
+ //mTicker.halt();
}
}
}
diff --git a/services/java/com/android/server/status/StatusBarNotification.java b/services/java/com/android/server/status/StatusBarNotification.java
index e5773f7..4bb8fdb 100644
--- a/services/java/com/android/server/status/StatusBarNotification.java
+++ b/services/java/com/android/server/status/StatusBarNotification.java
@@ -19,7 +19,7 @@
import android.os.IBinder;
import android.view.View;
-class StatusBarNotification {
+public class StatusBarNotification {
IBinder key;
NotificationData data;
View view;