diff options
author | Joe Onorato <joeo@android.com> | 2010-04-16 09:20:55 -0700 |
---|---|---|
committer | Joe Onorato <joeo@android.com> | 2010-06-02 14:48:41 -0700 |
commit | 503007dd023668b1e45de948d3673e594d7f5a82 (patch) | |
tree | 9c1dc26b1716d0f805a832a116f494f51b3fdfe9 | |
parent | 94c98c0ed5db12cedcd3f48f63708b76e5fb1398 (diff) | |
download | frameworks_base-503007dd023668b1e45de948d3673e594d7f5a82.zip frameworks_base-503007dd023668b1e45de948d3673e594d7f5a82.tar.gz frameworks_base-503007dd023668b1e45de948d3673e594d7f5a82.tar.bz2 |
Checkpoint status bar factoring. Now it builds and doesn't crash at boot.
Change-Id: I23f2045abfec0b414d5381f5e609b7267da7f21a
56 files changed, 4887 insertions, 181 deletions
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 Binary files differnew file mode 100644 index 0000000..baafed6 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal.9.png 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 Binary files differnew file mode 100644 index 0000000..175197b --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable.9.png 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 Binary files differnew file mode 100644 index 0000000..ec1feff --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_normal_disable_focused.9.png 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 Binary files differnew file mode 100644 index 0000000..c1f9a0f --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_pressed.9.png 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 Binary files differnew file mode 100644 index 0000000..0ea3f40 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/btn_default_small_selected.9.png diff --git a/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png b/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png Binary files differnew file mode 100755 index 0000000..1c7f9db --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/stat_notify_more.png 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 Binary files differnew file mode 100644 index 0000000..5acf638 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_close_on.9.png 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 Binary files differnew file mode 100644 index 0000000..be36ff2 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/status_bar_header_background.9.png diff --git a/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png b/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png Binary files differnew file mode 100644 index 0000000..dcca695 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/statusbar_background.9.png diff --git a/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png Binary files differnew file mode 100644 index 0000000..70f7cc2 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_portrait.9.png diff --git a/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png Binary files differnew file mode 100644 index 0000000..e6dab63 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-hdpi/title_bar_shadow.9.png 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 Binary files differnew file mode 100644 index 0000000..bcedd5f --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal.9.png 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 Binary files differnew file mode 100644 index 0000000..ac6260f --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable.9.png 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 Binary files differnew file mode 100644 index 0000000..4ee1b3f --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_normal_disable_focused.9.png 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 Binary files differnew file mode 100644 index 0000000..25e38f4 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_pressed.9.png 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 Binary files differnew file mode 100644 index 0000000..cc209c6 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/btn_default_small_selected.9.png diff --git a/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png b/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png Binary files differnew file mode 100644 index 0000000..e129ba9 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/stat_notify_more.png 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 Binary files differnew file mode 100644 index 0000000..9cbd9fe --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_close_on.9.png 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 Binary files differnew file mode 100644 index 0000000..fa9a90c --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/status_bar_header_background.9.png diff --git a/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png b/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png Binary files differnew file mode 100644 index 0000000..eb7c1a4 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/statusbar_background.9.png diff --git a/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png Binary files differnew file mode 100644 index 0000000..13b18d8 --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_portrait.9.png diff --git a/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png Binary files differnew file mode 100644 index 0000000..dbcefee --- /dev/null +++ b/packages/StatusBarPhone/res/drawable-mdpi/title_bar_shadow.9.png 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 index 2237ee4..2237ee4 100644 --- a/core/res/res/layout/status_bar.xml +++ b/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/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java new file mode 100644 index 0000000..36e1bfb --- /dev/null +++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/LatestItemView.java @@ -0,0 +1,34 @@ +/* + * 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.util.Slog; +import android.view.MotionEvent; +import android.widget.FrameLayout; + +public class LatestItemView extends FrameLayout { + + public LatestItemView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + 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/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java new file mode 100644 index 0000000..a5d0c6a --- /dev/null +++ b/packages/StatusBarPhone/src/com/android/policy/statusbar/phone/NotificationLinearLayout.java @@ -0,0 +1,29 @@ +/* + * 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.widget.LinearLayout; + + +public class NotificationLinearLayout extends LinearLayout { + public NotificationLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } +} + 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 @@ public class PhoneStatusBarService extends StatusBarService { 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.Window; 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 @@ public abstract class StatusBarService extends Service { * 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 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.ViewGroup; 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 @@ public class StatusBarView extends FrameLayout { @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 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 android.widget.ImageSwitcher; 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 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.util.Slog; 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.ImageView; 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub mContext = context; mDisplay = ((WindowManager)context.getSystemService( Context.WINDOW_SERVICE)).getDefaultDisplay(); - makeStatusBarView(context); mUninstallReceiver = new UninstallReceiver(); } @@ -251,106 +248,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub // ================================================================================ // 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub // 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub /* 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub || !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 @@ public class StatusBarManagerService extends IStatusBarService.Stub } /* 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } - 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub } 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } 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 @@ package com.android.server.status; import android.os.IBinder; import android.view.View; -class StatusBarNotification { +public class StatusBarNotification { IBinder key; NotificationData data; View view; |