diff options
Diffstat (limited to 'packages/SystemUI')
33 files changed, 2429 insertions, 1518 deletions
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 75045d7..6d77a3a 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -12,7 +12,12 @@ android:icon="@drawable/ic_launcher_settings"> <service - android:name=".statusbar.StatusBarService" + android:name=".statusbar.PhoneStatusBarService" + android:exported="false" + /> + + <service + android:name=".statusbar.tablet.TabletStatusBarService" android:exported="false" /> diff --git a/packages/SystemUI/res/drawable/dots_empty.png b/packages/SystemUI/res/drawable/dots_empty.png Binary files differnew file mode 100644 index 0000000..22ada41 --- /dev/null +++ b/packages/SystemUI/res/drawable/dots_empty.png diff --git a/packages/SystemUI/res/drawable/dots_full.png b/packages/SystemUI/res/drawable/dots_full.png Binary files differnew file mode 100644 index 0000000..2a346d6 --- /dev/null +++ b/packages/SystemUI/res/drawable/dots_full.png diff --git a/packages/SystemUI/res/drawable/notification_dragger.png b/packages/SystemUI/res/drawable/notification_dragger.png Binary files differnew file mode 100644 index 0000000..fad1f32 --- /dev/null +++ b/packages/SystemUI/res/drawable/notification_dragger.png diff --git a/packages/SystemUI/res/drawable/status_bar_back.xml b/packages/SystemUI/res/drawable/status_bar_back.xml new file mode 100644 index 0000000..92bf147 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_back.xml @@ -0,0 +1,21 @@ +<?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_pressed="true" android:drawable="@drawable/status_bar_back_pressed" /> + <item android:drawable="@drawable/status_bar_back_default" /> +</selector> + diff --git a/packages/SystemUI/res/drawable/status_bar_back_default.png b/packages/SystemUI/res/drawable/status_bar_back_default.png Binary files differnew file mode 100644 index 0000000..dd64746 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_back_default.png diff --git a/packages/SystemUI/res/drawable/status_bar_back_pressed.png b/packages/SystemUI/res/drawable/status_bar_back_pressed.png Binary files differnew file mode 100644 index 0000000..66a3677 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_back_pressed.png diff --git a/packages/SystemUI/res/drawable/status_bar_home.xml b/packages/SystemUI/res/drawable/status_bar_home.xml new file mode 100644 index 0000000..0011711 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_home.xml @@ -0,0 +1,21 @@ +<?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_pressed="true" android:drawable="@drawable/status_bar_home_pressed" /> + <item android:drawable="@drawable/status_bar_home_default" /> +</selector> + diff --git a/packages/SystemUI/res/drawable/status_bar_home_default.png b/packages/SystemUI/res/drawable/status_bar_home_default.png Binary files differnew file mode 100644 index 0000000..b129210 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_home_default.png diff --git a/packages/SystemUI/res/drawable/status_bar_home_pressed.png b/packages/SystemUI/res/drawable/status_bar_home_pressed.png Binary files differnew file mode 100644 index 0000000..dcb2447 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_home_pressed.png diff --git a/packages/SystemUI/res/drawable/status_bar_icon_tray.9.png b/packages/SystemUI/res/drawable/status_bar_icon_tray.9.png Binary files differnew file mode 100644 index 0000000..07d00cb --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_icon_tray.9.png diff --git a/packages/SystemUI/res/drawable/status_bar_menu.xml b/packages/SystemUI/res/drawable/status_bar_menu.xml new file mode 100644 index 0000000..aa7286e --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_menu.xml @@ -0,0 +1,21 @@ +<?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_pressed="true" android:drawable="@drawable/status_bar_menu_pressed" /> + <item android:drawable="@drawable/status_bar_menu_default" /> +</selector> + diff --git a/packages/SystemUI/res/drawable/status_bar_menu_default.png b/packages/SystemUI/res/drawable/status_bar_menu_default.png Binary files differnew file mode 100644 index 0000000..bf3a755 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_menu_default.png diff --git a/packages/SystemUI/res/drawable/status_bar_menu_pressed.png b/packages/SystemUI/res/drawable/status_bar_menu_pressed.png Binary files differnew file mode 100644 index 0000000..15e21d73 --- /dev/null +++ b/packages/SystemUI/res/drawable/status_bar_menu_pressed.png diff --git a/packages/SystemUI/res/layout-xlarge/status_bar.xml b/packages/SystemUI/res/layout-xlarge/status_bar.xml new file mode 100644 index 0000000..4d82d84 --- /dev/null +++ b/packages/SystemUI/res/layout-xlarge/status_bar.xml @@ -0,0 +1,194 @@ +<?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. +*/ +--> + +<!-- android:background="@drawable/status_bar_closed_default_background" --> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" + android:background="@drawable/status_bar_background" + android:focusable="true" + android:descendantFocusability="afterDescendants" + > + +<!-- + <LinearLayout android:id="@+id/statusIcons" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentRight="true" + android:paddingRight="6dip" + android:gravity="center_vertical" + android:orientation="horizontal"/> +--> + + <com.android.systemui.statusbar.tablet.NotificationIconArea + android:id="@+id/notificationIcons" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentLeft="true" + android:paddingLeft="6dip" + android:gravity="center_vertical" + android:orientation="horizontal" + android:clickable="true" + android:onClick="notificationIconsClicked" + android:background="@drawable/status_bar_icon_tray" + > + <view + class="com.android.systemui.statusbar.tablet.NotificationIconArea$MoreView" + android:id="@+id/more" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:src="@drawable/stat_notify_more" + android:layout_marginLeft="10dip" + /> + <view + class="com.android.systemui.statusbar.tablet.NotificationIconArea$IconLayout" + android:id="@+id/icons" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginLeft="8dip" + /> + <view + class="com.android.systemui.statusbar.tablet.NotificationIconArea$DraggerView" + android:id="@+id/handle" + android:layout_width="24dip" + android:layout_height="match_parent" + android:layout_marginLeft="8dip" + /> + + </com.android.systemui.statusbar.tablet.NotificationIconArea> + + <RelativeLayout android:id="@+id/systemInfo" + android:layout_width="200dip" + android:layout_height="match_parent" + android:layout_centerHorizontal="true" + android:clickable="true" + android:onClick="systemInfoClicked" + > + <com.android.systemui.statusbar.Clock + android:id="@+id/clock" + android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:singleLine="true" + android:textSize="16sp" + android:textStyle="bold" + android:padding="6dip" + /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toLeftOf="@id/clock" + android:layout_centerVertical="true" + android:src="@drawable/dots_empty" + /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_toRightOf="@id/clock" + android:layout_centerVertical="true" + android:src="@drawable/dots_full" + /> + </RelativeLayout> + + <com.android.systemui.statusbar.KeyButtonView android:id="@+id/back" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_toLeftOf="@+id/menu" + android:paddingLeft="4dip" + android:paddingRight="4dip" + android:src="@drawable/status_bar_back" + systemui:keyCode="4" + /> + <com.android.systemui.statusbar.KeyButtonView android:id="@+id/menu" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_toLeftOf="@+id/home" + android:src="@drawable/status_bar_menu" + android:paddingLeft="4dip" + android:paddingRight="4dip" + systemui:keyCode="82" + /> + <com.android.systemui.statusbar.KeyButtonView android:id="@+id/home" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentRight="true" + android:paddingLeft="4dip" + android:paddingRight="4dip" + android:src="@drawable/status_bar_home" + systemui:keyCode="3" + /> + +<!-- + + <LinearLayout android:id="@+id/ticker" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingLeft="6dip" + android:animationCache="false" + android:orientation="horizontal" > + <ImageSwitcher android:id="@+id/tickerIcon" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginRight="8dip" + > + <com.android.systemui.statusbar.AnimatedImageView + android:layout_width="25dip" + android:layout_height="25dip" + /> + <com.android.systemui.statusbar.AnimatedImageView + android:layout_width="25dip" + android:layout_height="25dip" + /> + </ImageSwitcher> + <com.android.systemui.statusbar.TickerView android:id="@+id/tickerText" + android:layout_width="0dip" + android:layout_weight="1" + android:layout_height="wrap_content" + android:paddingTop="2dip" + android:paddingRight="10dip"> + <TextView + android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + /> + <TextView + android:textAppearance="@*android:style/TextAppearance.StatusBar.Ticker" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:singleLine="true" + /> + </com.android.systemui.statusbar.TickerView> + </LinearLayout> + + <com.android.systemui.statusbar.DateView android:id="@+id/date" + android:textAppearance="@*android:style/TextAppearance.StatusBar.Icon" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:singleLine="true" + android:gravity="center_vertical|left" + android:paddingLeft="6px" + android:paddingRight="6px" + android:background="@drawable/status_bar_background" + /> +--> +</RelativeLayout> + diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml new file mode 100644 index 0000000..4a34f03 --- /dev/null +++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml @@ -0,0 +1,53 @@ +<?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. +*/ +--> + +<!-- android:background="@drawable/status_bar_closed_default_background" --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingLeft="8dip" + android:paddingRight="8dip" + > + + <LinearLayout + android:id="@+id/content" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_horizontal|bottom" + android:animationCache="false" + android:orientation="vertical" + android:background="@drawable/status_bar_background" + android:clickable="true" + android:focusable="true" + android:descendantFocusability="afterDescendants" + > + <TextView + android:id="@+id/notificationPanelDummy" + android:layout_width="wrap_content" + android:layout_height="75dip" + android:layout_centerInParent="true" + android:gravity="center" + android:textColor="#FFCCCCCC" + android:textSize="18sp" + android:padding="4dip" + /> + </LinearLayout> +</FrameLayout> diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml new file mode 100644 index 0000000..2222d08 --- /dev/null +++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_system.xml @@ -0,0 +1,52 @@ +<?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. +*/ +--> + +<!-- android:background="@drawable/status_bar_closed_default_background" --> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="300dip" + android:layout_width="400dip" + android:paddingLeft="8dip" + android:paddingRight="8dip" + android:background="#FF000000" + > + + <RelativeLayout + android:id="@+id/content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:animationCache="false" + android:background="@drawable/status_bar_background" + android:clickable="true" + android:focusable="true" + android:descendantFocusability="afterDescendants" + > + + <TextView + android:id="@+id/systemPanelDummy" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" + android:textColor="#FFCCCCCC" + android:textSize="18sp" + /> + + </RelativeLayout> +</FrameLayout> diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 5fe8e79..2f1b36e 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -21,6 +21,7 @@ <!-- android:background="@drawable/status_bar_closed_default_background" --> <com.android.systemui.statusbar.StatusBarView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:background="@drawable/status_bar_background" android:orientation="vertical" android:focusable="true" diff --git a/packages/SystemUI/res/values-xlarge/colors.xml b/packages/SystemUI/res/values-xlarge/colors.xml new file mode 100644 index 0000000..14161c3 --- /dev/null +++ b/packages/SystemUI/res/values-xlarge/colors.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <drawable name="status_bar_background">#000000</drawable> +</resources> + diff --git a/packages/SystemUI/res/values-xlarge/config.xml b/packages/SystemUI/res/values-xlarge/config.xml new file mode 100644 index 0000000..4cf5d18d11 --- /dev/null +++ b/packages/SystemUI/res/values-xlarge/config.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2010, 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. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <integer name="config_status_bar_position">1</integer> +</resources> + diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml new file mode 100644 index 0000000..23bcf20 --- /dev/null +++ b/packages/SystemUI/res/values/attrs.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 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> + <declare-styleable name="KeyButtonView"> + <attr name="keyCode" format="integer" /> + </declare-styleable> +</resources> + diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 8ea46e5..ac00c69 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -20,7 +20,16 @@ <!-- These resources are around just to allow their values to be customized for different hardware and product builds. --> <resources> - <!-- Control whether status bar should distinguish HSPA data icon form UMTS data icon on devices --> + + <!-- Control whether status bar should distinguish HSPA data icon form UMTS + data icon on devices --> <bool name="config_hspa_data_distinguishable">false</bool> + + <!-- The location of the status bar. + 0 - top + 1 - bottom + --> + <integer name="config_status_bar_position">0</integer> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java index f45caf5..0f6723e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CloseDragHandle.java @@ -23,7 +23,7 @@ import android.widget.LinearLayout; public class CloseDragHandle extends LinearLayout { - StatusBarService mService; + PhoneStatusBarService mService; public CloseDragHandle(Context context, AttributeSet attrs) { super(context, attrs); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index f9347b1..2c0af65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -32,7 +32,7 @@ import com.android.internal.statusbar.StatusBarNotification; * coalescing these calls so they don't stack up. For the calls * are coalesced, note that they are all idempotent. */ -class CommandQueue extends IStatusBar.Stub { +public class CommandQueue extends IStatusBar.Stub { private static final String TAG = "StatusBar.CommandQueue"; private static final int MSG_MASK = 0xffff0000; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java index 3d85f27..a2d4b95 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandedView.java @@ -27,7 +27,7 @@ import android.util.Slog; public class ExpandedView extends LinearLayout { - StatusBarService mService; + PhoneStatusBarService mService; int mPrevHeight = -1; public ExpandedView(Context context, AttributeSet attrs) { @@ -53,7 +53,7 @@ public class ExpandedView extends LinearLayout { //Slog.d(StatusBarService.TAG, "height changed old=" + mPrevHeight // + " new=" + height); mPrevHeight = height; - mService.updateExpandedViewPos(StatusBarService.EXPANDED_LEAVE_ALONE); + mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java new file mode 100644 index 0000000..4ab91b0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyButtonView.java @@ -0,0 +1,116 @@ +/* + * 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.systemui.statusbar; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.AnimationDrawable; +import android.graphics.drawable.Drawable; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.ServiceManager; +import android.util.AttributeSet; +import android.util.Slog; +import android.view.IWindowManager; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.widget.ImageView; +import android.widget.RemoteViews.RemoteView; + +import com.android.systemui.R; + +public class KeyButtonView extends ImageView { + IWindowManager mWindowManager; + long mDownTime; + boolean mSending; + int mCode; + int mRepeat; + + public KeyButtonView(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public KeyButtonView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs); + + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.KeyButtonView, + defStyle, 0); + + mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0); + if (mCode == 0) { + Slog.w(StatusBarService.TAG, "KeyButtonView without key code id=0x" + + Integer.toHexString(getId())); + } + + a.recycle(); + + mWindowManager = IWindowManager.Stub.asInterface( + ServiceManager.getService(Context.WINDOW_SERVICE)); + } + + public boolean onTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + int x, y; + + switch (action) { + case MotionEvent.ACTION_DOWN: + mDownTime = SystemClock.uptimeMillis(); + mRepeat = 0; + mSending = true; + sendEvent(KeyEvent.ACTION_DOWN, mDownTime); + setPressed(true); + break; + case MotionEvent.ACTION_MOVE: + if (mSending) { + x = (int)ev.getX(); + y = (int)ev.getY(); + if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) { + mSending = false; + sendEvent(KeyEvent.ACTION_UP); + setPressed(false); + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + if (mSending) { + mSending = false; + sendEvent(KeyEvent.ACTION_UP); + setPressed(false); + } + break; + } + + return true; + } + + void sendEvent(int action) { + sendEvent(action, SystemClock.uptimeMillis()); + } + + void sendEvent(int action, long when) { + final KeyEvent ev = new KeyEvent(mDownTime, mDownTime, action, mCode, mRepeat); + try { + Slog.d(StatusBarService.TAG, "injecting event " + ev); + mWindowManager.injectKeyEvent(ev, false); + } catch (RemoteException ex) { + // System process is dead + } + } +} + + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java new file mode 100644 index 0000000..eff9a1d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/PhoneStatusBarService.java @@ -0,0 +1,1552 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.app.Service; +import android.app.ActivityManagerNative; +import android.app.Dialog; +import android.app.Notification; +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.Binder; +import android.os.Handler; +import android.os.Message; +import android.os.SystemClock; +import android.util.Slog; +import android.util.Log; +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.ImageView; +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; + +import com.android.internal.statusbar.IStatusBar; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.statusbar.StatusBarIconList; +import com.android.internal.statusbar.StatusBarNotification; + +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.StatusBarPolicy; + + + +public class PhoneStatusBarService extends StatusBarService { + static final String TAG = "PhoneStatusBarService"; + 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 MSG_SHOW_INTRUDER = 1002; + private static final int MSG_HIDE_INTRUDER = 1003; + + // will likely move to a resource or other tunable param at some point + private static final int INTRUDER_ALERT_DECAY_MS = 10000; + + StatusBarPolicy mIconPolicy; + + int mIconSize; + Display mDisplay; + + StatusBarView mStatusBarView; + int mPixelFormat; + H mHandler = new H(); + Object mQueueLock = new Object(); + + // icons + LinearLayout mIcons; + IconMerger mNotificationIcons; + LinearLayout mStatusIcons; + + // expanded notifications + Dialog mExpandedDialog; + ExpandedView mExpandedView; + WindowManager.LayoutParams mExpandedParams; + ScrollView mScrollView; + View mNotificationLinearLayout; + View mExpandedContents; + // top bar + TextView mNoNotificationsTitle; + TextView mClearButton; + // drag bar + CloseDragHandle mCloseView; + // ongoing + NotificationData mOngoing = new NotificationData(); + TextView mOngoingTitle; + LinearLayout mOngoingItems; + // latest + NotificationData mLatest = new NotificationData(); + TextView mLatestTitle; + LinearLayout mLatestItems; + // position + int[] mPositionTmp = new int[2]; + boolean mExpanded; + boolean mExpandedVisible; + + // the date view + DateView mDateView; + + // for immersive activities + private View mIntruderAlertView; + + // the tracker view + TrackingView mTrackingView; + WindowManager.LayoutParams mTrackingParams; + int mTrackingPosition; // the position of the top of the tracking view. + private boolean mPanelSlightlyVisible; + + // 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 + int mDisabled = 0; + + 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) { + animateCollapse(); + } + return true; + } + return super.dispatchKeyEvent(event); + } + } + + @Override + public void onCreate() { + mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); + + super.onCreate(); + + addIntruderView(); + + // Lastly, call to the icon policy to install/update all the icons. + mIconPolicy = new StatusBarPolicy(this); + } + + @Override + public void onDestroy() { + // we're never destroyed + } + + // ================================================================================ + // Constructing the view + // ================================================================================ + protected View makeStatusBarView() { + final Context context = this; + + Resources res = context.getResources(); + + mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); + + ExpandedView expanded = (ExpandedView)View.inflate(context, + R.layout.status_bar_expanded, null); + expanded.mService = this; + + mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null); + mIntruderAlertView.setVisibility(View.GONE); + mIntruderAlertView.setClickable(true); + + 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); + 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); + 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, 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); + + // the more notifications icon + StatusBarIconView moreView = new StatusBarIconView(this, "more"); + moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0)); + mNotificationIcons.addMoreView(moreView, + new LinearLayout.LayoutParams(mIconSize, mIconSize)); + + // set the inital view visibility + setAreThereNotifications(); + mDateView.setVisibility(View.INVISIBLE); + + // 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); + context.registerReceiver(mBroadcastReceiver, filter); + + return sb; + } + + protected int getStatusBarGravity() { + return Gravity.TOP | Gravity.FILL_HORIZONTAL; + } + + private void addIntruderView() { + final Resources res = getResources(); + final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + PixelFormat.TRANSLUCENT); + lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + lp.y += height * 1.5; // FIXME + lp.setTitle("IntruderAlert"); + lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert; + + WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp); + } + + public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { + if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex + + " icon=" + icon); + StatusBarIconView view = new StatusBarIconView(this, slot); + view.set(icon); + mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); + } + + public void updateIcon(String slot, int index, int viewIndex, + StatusBarIcon old, StatusBarIcon icon) { + if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex + + " old=" + old + " icon=" + icon); + StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); + view.set(icon); + } + + public void removeIcon(String slot, int index, int viewIndex) { + if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); + mStatusIcons.removeViewAt(viewIndex); + } + + public void addNotification(IBinder key, StatusBarNotification notification) { + StatusBarIconView iconView = addNotificationViews(key, notification); + if (iconView == null) return; + + boolean immersive = false; + try { + immersive = ActivityManagerNative.getDefault().isTopActivityImmersive(); + Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive")); + } catch (RemoteException ex) { + } + if (immersive) { + if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) { + Slog.d(TAG, "Presenting high-priority notification in immersive activity"); + // @@@ special new transient ticker mode + // 1. Populate mIntruderAlertView + + ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon); + TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText); + alertIcon.setImageDrawable(StatusBarIconView.getIcon( + alertIcon.getContext(), + iconView.getStatusBarIcon())); + alertText.setText(notification.notification.tickerText); + + View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content); + button.setOnClickListener( + new Launcher(notification.notification.contentIntent, + notification.pkg, notification.tag, notification.id)); + + // 2. Animate mIntruderAlertView in + mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER); + + // 3. Set alarm to age the notification off (TODO) + mHandler.removeMessages(MSG_HIDE_INTRUDER); + mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS); + } + } else if (notification.notification.fullScreenIntent != null) { + // not immersive & a full-screen alert should be shown + Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;" + + " sending fullScreenIntent"); + try { + notification.notification.fullScreenIntent.send(); + } catch (PendingIntent.CanceledException e) { + } + } else { + // usual case: status bar visible & not immersive + + // show the ticker + tick(notification); + } + + // Recalculate the position of the sliding windows and the titles. + setAreThereNotifications(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + } + + public void updateNotification(IBinder key, StatusBarNotification notification) { + Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification); + + NotificationData oldList; + int oldIndex = mOngoing.findEntry(key); + if (oldIndex >= 0) { + oldList = mOngoing; + } else { + oldIndex = mLatest.findEntry(key); + if (oldIndex < 0) { + Slog.w(TAG, "updateNotification for unknown key: " + key); + return; + } + oldList = mLatest; + } + final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex); + final StatusBarNotification oldNotification = oldEntry.notification; + final RemoteViews oldContentView = oldNotification.notification.contentView; + + final RemoteViews contentView = notification.notification.contentView; + + if (false) { + Slog.d(TAG, "old notification: when=" + oldNotification.notification.when + + " ongoing=" + oldNotification.isOngoing() + + " expanded=" + oldEntry.expanded + + " contentView=" + oldContentView); + Slog.d(TAG, "new notification: when=" + notification.notification.when + + " ongoing=" + oldNotification.isOngoing() + + " contentView=" + contentView); + } + + // Can we just reapply the RemoteViews in place? If when didn't change, the order + // didn't change. + if (notification.notification.when == oldNotification.notification.when + && notification.isOngoing() == oldNotification.isOngoing() + && oldEntry.expanded != null + && contentView != null && oldContentView != null + && contentView.getPackage() != null + && oldContentView.getPackage() != null + && oldContentView.getPackage().equals(contentView.getPackage()) + && oldContentView.getLayoutId() == contentView.getLayoutId()) { + if (SPEW) Slog.d(TAG, "reusing notification"); + oldEntry.notification = notification; + try { + // Reapply the RemoteViews + contentView.reapply(this, oldEntry.content); + // update the contentIntent + final PendingIntent contentIntent = notification.notification.contentIntent; + if (contentIntent != null) { + oldEntry.content.setOnClickListener(new Launcher(contentIntent, + notification.pkg, notification.tag, notification.id)); + } + // Update the icon. + final StatusBarIcon ic = new StatusBarIcon(notification.pkg, + notification.notification.icon, notification.notification.iconLevel, + notification.notification.number); + if (!oldEntry.icon.set(ic)) { + handleNotificationError(key, notification, "Couldn't update icon: " + ic); + return; + } + } + 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 " + contentView.getPackage(), e); + removeNotificationViews(key); + addNotificationViews(key, notification); + } + } else { + if (SPEW) Slog.d(TAG, "not reusing notification"); + removeNotificationViews(key); + addNotificationViews(key, notification); + } + + // Restart the ticker if it's still running + tick(notification); + + // Recalculate the position of the sliding windows and the titles. + setAreThereNotifications(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + } + + public void removeNotification(IBinder key) { + if (SPEW) Slog.d(TAG, "removeNotification key=" + key); + StatusBarNotification old = removeNotificationViews(key); + + if (old != null) { + // Cancel the ticker if it's still running + mTicker.removeEntry(old); + + // Recalculate the position of the sliding windows and the titles. + setAreThereNotifications(); + updateExpandedViewPos(EXPANDED_LEAVE_ALONE); + } + } + + private int chooseIconIndex(boolean isOngoing, int viewIndex) { + final int latestSize = mLatest.size(); + if (isOngoing) { + return latestSize + (mOngoing.size() - viewIndex); + } else { + return latestSize - viewIndex; + } + } + + View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) { + Notification n = notification.notification; + 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(R.layout.status_bar_latest_event, parent, false); + + // bind the click event to the content area + ViewGroup content = (ViewGroup)row.findViewById(R.id.content); + content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); + content.setOnFocusChangeListener(mFocusChangeListener); + PendingIntent contentIntent = n.contentIntent; + if (contentIntent != null) { + content.setOnClickListener(new Launcher(contentIntent, notification.pkg, + notification.tag, notification.id)); + } + + View expanded = null; + Exception exception = null; + try { + expanded = remoteViews.apply(this, content); + } + catch (RuntimeException e) { + exception = e; + } + if (expanded == null) { + String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id); + Slog.e(TAG, "couldn't inflate view for notification " + ident, exception); + return null; + } else { + content.addView(expanded); + row.setDrawingCacheEnabled(true); + } + + return new View[] { row, content, expanded }; + } + + StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) { + NotificationData list; + ViewGroup parent; + final boolean isOngoing = notification.isOngoing(); + if (isOngoing) { + list = mOngoing; + parent = mOngoingItems; + } else { + list = mLatest; + parent = mLatestItems; + } + // Construct the expanded view. + final View[] views = makeNotificationView(notification, parent); + if (views == null) { + handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " + + notification); + return null; + } + final View row = views[0]; + final View content = views[1]; + final View expanded = views[2]; + // Construct the icon. + final StatusBarIconView iconView = new StatusBarIconView(this, + notification.pkg + "/0x" + Integer.toHexString(notification.id)); + final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, + notification.notification.iconLevel, notification.notification.number); + if (!iconView.set(ic)) { + handleNotificationError(key, notification, "Coulding create icon: " + ic); + return null; + } + // Add the expanded view. + final int viewIndex = list.add(key, notification, row, content, expanded, iconView); + parent.addView(row, viewIndex); + // Add the icon. + final int iconIndex = chooseIconIndex(isOngoing, viewIndex); + mNotificationIcons.addView(iconView, iconIndex, + new LinearLayout.LayoutParams(mIconSize, mIconSize)); + return iconView; + } + + StatusBarNotification removeNotificationViews(IBinder key) { + NotificationData.Entry entry = mOngoing.remove(key); + if (entry == null) { + entry = mLatest.remove(key); + if (entry == null) { + Slog.w(TAG, "removeNotification for unknown key: " + key); + return null; + } + } + // Remove the expanded view. + ((ViewGroup)entry.row.getParent()).removeView(entry.row); + // Remove the icon. + ((ViewGroup)entry.icon.getParent()).removeView(entry.icon); + + return entry.notification; + } + + private void setAreThereNotifications() { + boolean ongoing = mOngoing.hasVisibleItems(); + boolean latest = mLatest.hasVisibleItems(); + + // (no ongoing notifications are clearable) + if (mLatest.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); + } + } + + + /** + * State is one or more of the DISABLE constants from StatusBarManager. + */ + public void disable(int state) { + final int old = mDisabled; + final int diff = state ^ old; + mDisabled = state; + + if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { + if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { + Slog.d(TAG, "DISABLE_EXPAND: yes"); + animateCollapse(); + } + } + if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { + if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { + Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); + if (mTicking) { + 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 && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { + Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes"); + mTicker.halt(); + } + } + } + + /** + * All changes to the status bar and notifications funnel through here and are batched. + */ + private class H extends Handler { + public void handleMessage(Message m) { + switch (m.what) { + case MSG_ANIMATE: + doAnimation(); + break; + case MSG_ANIMATE_REVEAL: + doRevealAnimation(); + break; + case MSG_SHOW_INTRUDER: + setIntruderAlertVisibility(true); + break; + case MSG_HIDE_INTRUDER: + setIntruderAlertVisibility(false); + break; + } + } + } + + 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); + } + }; + + private void makeExpandedVisible() { + if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); + if (mExpandedVisible) { + return; + } + mExpandedVisible = true; + visibilityChanged(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); + } + } + + public 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); + } + + public 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; + } + + 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; + visibilityChanged(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) { + } + + if (mIntent != null) { + 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); + } catch (PendingIntent.CanceledException e) { + // the stack trace isn't very helpful here. Just log the exception message. + Slog.w(TAG, "Sending contentIntent failed: " + e); + } + } + + try { + mBarService.onNotificationClick(mPkg, mTag, mId); + } catch (RemoteException ex) { + // system process is dead if we're here. + } + + // close the shade if it was open + animateCollapse(); + + // If this click was on the intruder alert, hide that instead + mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); + } + } + + private void tick(StatusBarNotification n) { + // Show the ticker if one is requested. 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.notification.tickerText != null && mStatusBarView.getWindowToken() != null) { + if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS + | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { + mTicker.addEntry(n); + } + } + } + + /** + * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService + * about the failure. + * + * WARNING: this will call back into us. Don't hold any locks. + */ + void handleNotificationError(IBinder key, StatusBarNotification n, String message) { + removeNotification(key); + try { + mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message); + } catch (RemoteException ex) { + // The end is nigh. + } + } + + 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); + 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 (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); + } + } + */ + + 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; + } + mExpandedDialog.getWindow().setAttributes(mExpandedParams); + + // As long as this isn't just a repositioning that's not supposed to affect + // the user's perception of what's showing, call to say that the visibility + // has changed. (Otherwise, someone else will call to do that). + if (expandedPosition != EXPANDED_LEAVE_ALONE) { + if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")"); + visibilityChanged(visible); + } + } + + 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) + */ + void visibilityChanged(boolean visible) { + if (mPanelSlightlyVisible != visible) { + mPanelSlightlyVisible = visible; + try { + mBarService.onPanelRevealed(); + } catch (RemoteException ex) { + // Won't fail unless the world has ended. + } + } + } + + 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) { + try { + mBarService.onClearAllNotifications(); + } catch (RemoteException ex) { + // system process is dead if we're here. + } + animateCollapse(); + } + }; + + 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)) { + //collapse(); + } + else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { + updateResources(); + } + } + }; + + private void setIntruderAlertVisibility(boolean vis) { + mIntruderAlertView.setVisibility(vis ? View.VISIBLE : 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(); + } + }; +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java index 4f39ee4..e828f68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarPolicy.java @@ -1033,6 +1033,7 @@ public class StatusBarPolicy { iconId = sWifiSignalImages[mLastWifiSignalLevel]; } + mService.setIcon("wifi", iconId, 0); // Show the icon since wi-fi is connected mService.setIconVisibility("wifi", true); @@ -1041,11 +1042,11 @@ public class StatusBarPolicy { mIsWifiConnected = false; iconId = sWifiSignalImages[0]; + mService.setIcon("wifi", iconId, 0); // Hide the icon since we're not connected mService.setIconVisibility("wifi", false); } - mService.setIcon("wifi", iconId, 0); } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { int iconId; final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java index 07bcce7..a64c3e7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java @@ -17,188 +17,53 @@ package com.android.systemui.statusbar; import android.app.Service; -import com.android.internal.statusbar.IStatusBar; -import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.StatusBarIcon; -import com.android.internal.statusbar.StatusBarIconList; -import com.android.internal.statusbar.StatusBarNotification; - -import android.app.ActivityManagerNative; -import android.app.Dialog; -import android.app.Notification; -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.Binder; -import android.os.Handler; -import android.os.Message; import android.os.ServiceManager; -import android.os.SystemClock; import android.util.Slog; import android.util.Log; -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.ImageView; -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; - -import com.android.systemui.R; -import com.android.systemui.statusbar.policy.StatusBarPolicy; +import com.android.internal.statusbar.IStatusBar; +import com.android.internal.statusbar.IStatusBarService; +import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.statusbar.StatusBarIconList; +import com.android.internal.statusbar.StatusBarNotification; +import com.android.systemui.R; -public class StatusBarService extends Service implements CommandQueue.Callbacks { +public abstract class StatusBarService extends Service implements CommandQueue.Callbacks { static final String TAG = "StatusBarService"; - 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 MSG_SHOW_INTRUDER = 1002; - private static final int MSG_HIDE_INTRUDER = 1003; - - // will likely move to a resource or other tunable param at some point - private static final int INTRUDER_ALERT_DECAY_MS = 10000; - - StatusBarPolicy mIconPolicy; - - CommandQueue mCommandQueue; - IStatusBarService mBarService; - - int mIconSize; - Display mDisplay; - StatusBarView mStatusBarView; - int mPixelFormat; - H mHandler = new H(); - Object mQueueLock = new Object(); - - // icons - LinearLayout mIcons; - IconMerger mNotificationIcons; - LinearLayout mStatusIcons; - - // expanded notifications - Dialog mExpandedDialog; - ExpandedView mExpandedView; - WindowManager.LayoutParams mExpandedParams; - ScrollView mScrollView; - View mNotificationLinearLayout; - View mExpandedContents; - // top bar - TextView mNoNotificationsTitle; - TextView mClearButton; - // drag bar - CloseDragHandle mCloseView; - // ongoing - NotificationData mOngoing = new NotificationData(); - TextView mOngoingTitle; - LinearLayout mOngoingItems; - // latest - NotificationData mLatest = new NotificationData(); - TextView mLatestTitle; - LinearLayout mLatestItems; - // position - 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. - private boolean mPanelSlightlyVisible; - // ticker - private Ticker mTicker; - private View mTickerView; - private boolean mTicking; + protected CommandQueue mCommandQueue; + protected IStatusBarService mBarService; - // Tracking finger for opening/closing. - int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore - boolean mTracking; - VelocityTracker mVelocityTracker; + // Up-call methods + protected abstract View makeStatusBarView(); + protected abstract int getStatusBarGravity(); - 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 - int mDisabled = 0; - - 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) { - animateCollapse(); - } - return true; - } - return super.dispatchKeyEvent(event); - } + /** + * Nobody binds to us. + */ + @Override + public IBinder onBind(Intent intent) { + return null; } - @Override public void onCreate() { // First set up our views and stuff. - mDisplay = ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay(); - makeStatusBarView(this); + View sb = makeStatusBarView(); // Connect in to the status bar manager service StatusBarIconList iconList = new StatusBarIconList(); @@ -236,1371 +101,22 @@ public class StatusBarService extends Service implements CommandQueue.Callbacks } // Put up the view - addStatusBarView(); - - // Lastly, call to the icon policy to install/update all the icons. - mIconPolicy = new StatusBarPolicy(this); - } - - @Override - public void onDestroy() { - // we're never destroyed - } - - // for immersive activities - private View mIntruderAlertView; - - /** - * Nobody binds to us. - */ - @Override - public IBinder onBind(Intent intent) { - return null; - } - - // ================================================================================ - // Constructing the view - // ================================================================================ - private void makeStatusBarView(Context context) { - Resources res = context.getResources(); - - mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); - - ExpandedView expanded = (ExpandedView)View.inflate(context, - R.layout.status_bar_expanded, null); - expanded.mService = this; - - mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null); - mIntruderAlertView.setVisibility(View.GONE); - mIntruderAlertView.setClickable(true); - - 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); - 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); - 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, 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); - - // the more notifications icon - StatusBarIconView moreView = new StatusBarIconView(this, "more"); - moreView.set(new StatusBarIcon(null, R.drawable.stat_notify_more, 0)); - mNotificationIcons.addMoreView(moreView, - new LinearLayout.LayoutParams(mIconSize, mIconSize)); - - // set the inital view visibility - setAreThereNotifications(); - mDateView.setVisibility(View.INVISIBLE); - - // 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); - context.registerReceiver(mBroadcastReceiver, filter); - } - - protected void addStatusBarView() { - Resources res = getResources(); + final Resources res = getResources(); final int height= res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); - final StatusBarView view = mStatusBarView; - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, height, WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING, PixelFormat.RGBX_8888); - lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; + lp.gravity = getStatusBarGravity(); lp.setTitle("StatusBar"); // TODO lp.windowAnimations = R.style.Animation_StatusBar; + WindowManagerImpl.getDefault().addView(sb, lp); - WindowManagerImpl.getDefault().addView(view, lp); - - lp = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN - | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, - PixelFormat.TRANSLUCENT); - lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; - lp.y += height * 1.5; // FIXME - lp.setTitle("IntruderAlert"); - lp.windowAnimations = com.android.internal.R.style.Animation_StatusBar_IntruderAlert; - - WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp); - } - - public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { - if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex - + " icon=" + icon); - StatusBarIconView view = new StatusBarIconView(this, slot); - view.set(icon); - mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); - } - - public void updateIcon(String slot, int index, int viewIndex, - StatusBarIcon old, StatusBarIcon icon) { - if (SPEW) Slog.d(TAG, "updateIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex - + " old=" + old + " icon=" + icon); - StatusBarIconView view = (StatusBarIconView)mStatusIcons.getChildAt(viewIndex); - view.set(icon); - } - - public void removeIcon(String slot, int index, int viewIndex) { - if (SPEW) Slog.d(TAG, "removeIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex); - mStatusIcons.removeViewAt(viewIndex); - } - - public void addNotification(IBinder key, StatusBarNotification notification) { - StatusBarIconView iconView = addNotificationViews(key, notification); - if (iconView == null) return; - - boolean immersive = false; - try { - immersive = ActivityManagerNative.getDefault().isTopActivityImmersive(); - Slog.d(TAG, "Top activity is " + (immersive?"immersive":"not immersive")); - } catch (RemoteException ex) { - } - if (immersive) { - if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) { - Slog.d(TAG, "Presenting high-priority notification in immersive activity"); - // @@@ special new transient ticker mode - // 1. Populate mIntruderAlertView - - ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon); - TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText); - alertIcon.setImageDrawable(StatusBarIconView.getIcon( - alertIcon.getContext(), - iconView.getStatusBarIcon())); - alertText.setText(notification.notification.tickerText); - - View button = mIntruderAlertView.findViewById(R.id.intruder_alert_content); - button.setOnClickListener( - new Launcher(notification.notification.contentIntent, - notification.pkg, notification.tag, notification.id)); - - // 2. Animate mIntruderAlertView in - mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER); - - // 3. Set alarm to age the notification off (TODO) - mHandler.removeMessages(MSG_HIDE_INTRUDER); - mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS); - } - } else if (notification.notification.fullScreenIntent != null) { - // not immersive & a full-screen alert should be shown - Slog.d(TAG, "Notification has fullScreenIntent and activity is not immersive;" - + " sending fullScreenIntent"); - try { - notification.notification.fullScreenIntent.send(); - } catch (PendingIntent.CanceledException e) { - } - } else { - // usual case: status bar visible & not immersive - - // show the ticker - tick(notification); - } - - // Recalculate the position of the sliding windows and the titles. - setAreThereNotifications(); - updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - } - - public void updateNotification(IBinder key, StatusBarNotification notification) { - Slog.d(TAG, "updateNotification key=" + key + " notification=" + notification); - - NotificationData oldList; - int oldIndex = mOngoing.findEntry(key); - if (oldIndex >= 0) { - oldList = mOngoing; - } else { - oldIndex = mLatest.findEntry(key); - if (oldIndex < 0) { - Slog.w(TAG, "updateNotification for unknown key: " + key); - return; - } - oldList = mLatest; - } - final NotificationData.Entry oldEntry = oldList.getEntryAt(oldIndex); - final StatusBarNotification oldNotification = oldEntry.notification; - final RemoteViews oldContentView = oldNotification.notification.contentView; - - final RemoteViews contentView = notification.notification.contentView; - - if (false) { - Slog.d(TAG, "old notification: when=" + oldNotification.notification.when - + " ongoing=" + oldNotification.isOngoing() - + " expanded=" + oldEntry.expanded - + " contentView=" + oldContentView); - Slog.d(TAG, "new notification: when=" + notification.notification.when - + " ongoing=" + oldNotification.isOngoing() - + " contentView=" + contentView); - } - - // Can we just reapply the RemoteViews in place? If when didn't change, the order - // didn't change. - if (notification.notification.when == oldNotification.notification.when - && notification.isOngoing() == oldNotification.isOngoing() - && oldEntry.expanded != null - && contentView != null && oldContentView != null - && contentView.getPackage() != null - && oldContentView.getPackage() != null - && oldContentView.getPackage().equals(contentView.getPackage()) - && oldContentView.getLayoutId() == contentView.getLayoutId()) { - if (SPEW) Slog.d(TAG, "reusing notification"); - oldEntry.notification = notification; - try { - // Reapply the RemoteViews - contentView.reapply(this, oldEntry.content); - // update the contentIntent - final PendingIntent contentIntent = notification.notification.contentIntent; - if (contentIntent != null) { - oldEntry.content.setOnClickListener(new Launcher(contentIntent, - notification.pkg, notification.tag, notification.id)); - } - // Update the icon. - final StatusBarIcon ic = new StatusBarIcon(notification.pkg, - notification.notification.icon, notification.notification.iconLevel, - notification.notification.number); - if (!oldEntry.icon.set(ic)) { - handleNotificationError(key, notification, "Couldn't update icon: " + ic); - return; - } - } - 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 " + contentView.getPackage(), e); - removeNotificationViews(key); - addNotificationViews(key, notification); - } - } else { - if (SPEW) Slog.d(TAG, "not reusing notification"); - removeNotificationViews(key); - addNotificationViews(key, notification); - } - - // Restart the ticker if it's still running - tick(notification); - - // Recalculate the position of the sliding windows and the titles. - setAreThereNotifications(); - updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - } - - public void removeNotification(IBinder key) { - if (SPEW) Slog.d(TAG, "removeNotification key=" + key); - StatusBarNotification old = removeNotificationViews(key); - - if (old != null) { - // Cancel the ticker if it's still running - mTicker.removeEntry(old); - - // Recalculate the position of the sliding windows and the titles. - setAreThereNotifications(); - updateExpandedViewPos(EXPANDED_LEAVE_ALONE); - } - } - - private int chooseIconIndex(boolean isOngoing, int viewIndex) { - final int latestSize = mLatest.size(); - if (isOngoing) { - return latestSize + (mOngoing.size() - viewIndex); - } else { - return latestSize - viewIndex; - } - } - - View[] makeNotificationView(StatusBarNotification notification, ViewGroup parent) { - Notification n = notification.notification; - 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(R.layout.status_bar_latest_event, parent, false); - - // bind the click event to the content area - ViewGroup content = (ViewGroup)row.findViewById(R.id.content); - content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS); - content.setOnFocusChangeListener(mFocusChangeListener); - PendingIntent contentIntent = n.contentIntent; - if (contentIntent != null) { - content.setOnClickListener(new Launcher(contentIntent, notification.pkg, - notification.tag, notification.id)); - } - - View expanded = null; - Exception exception = null; - try { - expanded = remoteViews.apply(this, content); - } - catch (RuntimeException e) { - exception = e; - } - if (expanded == null) { - String ident = notification.pkg + "/0x" + Integer.toHexString(notification.id); - Slog.e(TAG, "couldn't inflate view for notification " + ident, exception); - return null; - } else { - content.addView(expanded); - row.setDrawingCacheEnabled(true); - } - - return new View[] { row, content, expanded }; - } - - StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) { - NotificationData list; - ViewGroup parent; - final boolean isOngoing = notification.isOngoing(); - if (isOngoing) { - list = mOngoing; - parent = mOngoingItems; - } else { - list = mLatest; - parent = mLatestItems; - } - // Construct the expanded view. - final View[] views = makeNotificationView(notification, parent); - if (views == null) { - handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " - + notification); - return null; - } - final View row = views[0]; - final View content = views[1]; - final View expanded = views[2]; - // Construct the icon. - final StatusBarIconView iconView = new StatusBarIconView(this, - notification.pkg + "/0x" + Integer.toHexString(notification.id)); - final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, - notification.notification.iconLevel, notification.notification.number); - if (!iconView.set(ic)) { - handleNotificationError(key, notification, "Coulding create icon: " + ic); - return null; - } - // Add the expanded view. - final int viewIndex = list.add(key, notification, row, content, expanded, iconView); - parent.addView(row, viewIndex); - // Add the icon. - final int iconIndex = chooseIconIndex(isOngoing, viewIndex); - mNotificationIcons.addView(iconView, iconIndex, - new LinearLayout.LayoutParams(mIconSize, mIconSize)); - return iconView; - } - - StatusBarNotification removeNotificationViews(IBinder key) { - NotificationData.Entry entry = mOngoing.remove(key); - if (entry == null) { - entry = mLatest.remove(key); - if (entry == null) { - Slog.w(TAG, "removeNotification for unknown key: " + key); - return null; - } - } - // Remove the expanded view. - ((ViewGroup)entry.row.getParent()).removeView(entry.row); - // Remove the icon. - ((ViewGroup)entry.icon.getParent()).removeView(entry.icon); - - return entry.notification; - } - - private void setAreThereNotifications() { - boolean ongoing = mOngoing.hasVisibleItems(); - boolean latest = mLatest.hasVisibleItems(); - - // (no ongoing notifications are clearable) - if (mLatest.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); - } - } - - - /** - * State is one or more of the DISABLE constants from StatusBarManager. - */ - public void disable(int state) { - final int old = mDisabled; - final int diff = state ^ old; - mDisabled = state; - - if ((diff & StatusBarManager.DISABLE_EXPAND) != 0) { - if ((state & StatusBarManager.DISABLE_EXPAND) != 0) { - Slog.d(TAG, "DISABLE_EXPAND: yes"); - animateCollapse(); - } - } - if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { - if ((state & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) { - Slog.d(TAG, "DISABLE_NOTIFICATION_ICONS: yes"); - if (mTicking) { - 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 && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) { - Slog.d(TAG, "DISABLE_NOTIFICATION_TICKER: yes"); - mTicker.halt(); - } - } - } - - /** - * All changes to the status bar and notifications funnel through here and are batched. - */ - private class H extends Handler { - public void handleMessage(Message m) { - switch (m.what) { - case MSG_ANIMATE: - doAnimation(); - break; - case MSG_ANIMATE_REVEAL: - doRevealAnimation(); - break; - case MSG_SHOW_INTRUDER: - setIntruderAlertVisibility(true); - break; - case MSG_HIDE_INTRUDER: - setIntruderAlertVisibility(false); - break; - } - } - } - - 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); - } - }; - - private void makeExpandedVisible() { - if (SPEW) Slog.d(TAG, "Make expanded visible: expanded visible=" + mExpandedVisible); - if (mExpandedVisible) { - return; - } - mExpandedVisible = true; - visibilityChanged(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); - } - } - - public 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); - } - - public 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; - } - - 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; - visibilityChanged(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); - } + Slog.d(TAG, "Added status bar view w/ gravity 0x" + Integer.toHexString(lp.gravity)); } - - 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) { - } - - if (mIntent != null) { - 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(StatusBarService.this, 0, overlay); - } catch (PendingIntent.CanceledException e) { - // the stack trace isn't very helpful here. Just log the exception message. - Slog.w(TAG, "Sending contentIntent failed: " + e); - } - } - - try { - mBarService.onNotificationClick(mPkg, mTag, mId); - } catch (RemoteException ex) { - // system process is dead if we're here. - } - - // close the shade if it was open - animateCollapse(); - - // If this click was on the intruder alert, hide that instead - mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER); - } - } - - private void tick(StatusBarNotification n) { - // Show the ticker if one is requested. 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.notification.tickerText != null && mStatusBarView.getWindowToken() != null) { - if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS - | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { - mTicker.addEntry(n); - } - } - } - - /** - * Cancel this notification and tell the StatusBarManagerService / NotificationManagerService - * about the failure. - * - * WARNING: this will call back into us. Don't hold any locks. - */ - void handleNotificationError(IBinder key, StatusBarNotification n, String message) { - removeNotification(key); - try { - mBarService.onNotificationError(n.pkg, n.tag, n.id, n.uid, n.initialPid, message); - } catch (RemoteException ex) { - // The end is nigh. - } - } - - 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(StatusBarService.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); - 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 (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); - } - } - */ - - 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; - } - mExpandedDialog.getWindow().setAttributes(mExpandedParams); - - // As long as this isn't just a repositioning that's not supposed to affect - // the user's perception of what's showing, call to say that the visibility - // has changed. (Otherwise, someone else will call to do that). - if (expandedPosition != EXPANDED_LEAVE_ALONE) { - if (SPEW) Slog.d(TAG, "updateExpandedViewPos visibilityChanged(" + visible + ")"); - visibilityChanged(visible); - } - } - - 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) - */ - void visibilityChanged(boolean visible) { - if (mPanelSlightlyVisible != visible) { - mPanelSlightlyVisible = visible; - try { - mBarService.onPanelRevealed(); - } catch (RemoteException ex) { - // Won't fail unless the world has ended. - } - } - } - - 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) { - try { - mBarService.onClearAllNotifications(); - } catch (RemoteException ex) { - // system process is dead if we're here. - } - animateCollapse(); - } - }; - - 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)) { - //collapse(); - } - else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) { - updateResources(); - } - } - }; - - private void setIntruderAlertVisibility(boolean vis) { - mIntruderAlertView.setVisibility(vis ? View.VISIBLE : 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(); - } - }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java index 1e140b9..20fc41f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarView.java @@ -19,6 +19,7 @@ package com.android.systemui.statusbar; import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; +import android.graphics.Rect; import android.os.SystemClock; import android.util.AttributeSet; import android.view.MotionEvent; @@ -34,7 +35,7 @@ public class StatusBarView extends FrameLayout { static final int DIM_ANIM_TIME = 400; - StatusBarService mService; + PhoneStatusBarService mService; boolean mTracking; int mStartX, mStartY; ViewGroup mNotificationIcons; @@ -46,6 +47,9 @@ public class StatusBarView extends FrameLayout { int mStartAlpha = 0, mEndAlpha = 0; long mEndTime = 0; + Rect mButtonBounds = new Rect(); + boolean mCapturingEvents = true; + public StatusBarView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -94,7 +98,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(StatusBarService.EXPANDED_LEAVE_ALONE); + mService.updateExpandedViewPos(PhoneStatusBarService.EXPANDED_LEAVE_ALONE); } @Override @@ -177,6 +181,9 @@ public class StatusBarView extends FrameLayout { */ @Override public boolean onTouchEvent(MotionEvent event) { + if (!mCapturingEvents) { + return false; + } if (event.getAction() != MotionEvent.ACTION_DOWN) { mService.interceptTouchEvent(event); } @@ -185,6 +192,13 @@ public class StatusBarView extends FrameLayout { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (mButtonBounds.contains((int)event.getX(), (int)event.getY())) { + mCapturingEvents = false; + return false; + } + } + mCapturingEvents = true; return mService.interceptTouchEvent(event) ? true : super.onInterceptTouchEvent(event); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java index 9108eee..c59eb6a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/TrackingView.java @@ -26,7 +26,7 @@ import android.widget.LinearLayout; public class TrackingView extends LinearLayout { final Display mDisplay; - StatusBarService mService; + PhoneStatusBarService mService; boolean mTracking; int mStartX, mStartY; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java new file mode 100644 index 0000000..3c7b130 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationIconArea.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tablet; + +import android.content.Context; +import android.content.res.Resources; +import android.os.Handler; +import android.util.AttributeSet; +import android.util.Slog; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.ImageView; + +import com.android.systemui.R; + + +public class NotificationIconArea extends LinearLayout { + private static final String TAG = "NotificationIconArea"; + + MoreView mMoreView; + IconLayout mIconLayout; + DraggerView mDraggerView; + + public NotificationIconArea(Context context, AttributeSet attrs) { + super(context, attrs); + + mMoreView = (MoreView) findViewById(R.id.more); + mIconLayout = (IconLayout)findViewById(R.id.icons); + mDraggerView = (DraggerView) findViewById(R.id.handle); + } + + static class MoreView extends ImageView { + public MoreView(Context context, AttributeSet attrs) { + super(context, attrs); + } + } + + static class IconLayout extends LinearLayout { + public IconLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + } + + static class DraggerView extends View { + public DraggerView(Context context, AttributeSet attrs) { + super(context, attrs); + } + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java new file mode 100644 index 0000000..3500729 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarService.java @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tablet; + +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.os.Handler; +import android.os.Message; +import android.os.IBinder; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.view.WindowManagerImpl; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.util.Slog; + +import com.android.internal.statusbar.StatusBarIcon; +import com.android.internal.statusbar.StatusBarIconList; +import com.android.internal.statusbar.StatusBarNotification; + +import com.android.systemui.statusbar.*; +import com.android.systemui.R; + +public class TabletStatusBarService extends StatusBarService { + public static final boolean DEBUG = false; + public static final String TAG = "TabletStatusBar"; + + View mStatusBarView; + NotificationIconArea mNotificationIconArea; + + int mIconSize; + + H mHandler = new H(); + + private View mNotificationPanel; + private View mSystemPanel; + + protected void addPanelWindows() { + if (mNotificationPanel == null) { + mNotificationPanel = View.inflate(this, R.layout.sysbar_panel_notifications, null); + mSystemPanel = View.inflate(this, R.layout.sysbar_panel_system, null); + } + + mNotificationPanel.setVisibility(View.GONE); + mSystemPanel.setVisibility(View.GONE); + + final Resources res = getResources(); + final int barHeight= res.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + 300, // ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + PixelFormat.TRANSLUCENT); + lp.gravity = Gravity.BOTTOM | Gravity.LEFT; + lp.setTitle("NotificationPanel"); + lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard; + + WindowManagerImpl.getDefault().addView(mNotificationPanel, lp); + + lp = new WindowManager.LayoutParams( + 400, // ViewGroup.LayoutParams.WRAP_CONTENT, + 200, // ViewGroup.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + PixelFormat.TRANSLUCENT); + lp.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + lp.setTitle("SystemPanel"); + lp.windowAnimations = com.android.internal.R.style.Animation_SlidingCard; + + WindowManagerImpl.getDefault().addView(mSystemPanel, lp); + + // Lorem ipsum, Dolores + TextView tv = ((TextView) mNotificationPanel.findViewById(R.id.notificationPanelDummy)); + if (tv != null) tv.setText("(You probably have new email)"); + tv = ((TextView) mSystemPanel.findViewById(R.id.systemPanelDummy)); + if (tv != null) tv.setText("System status: great"); + } + + @Override + public void onCreate() { + super.onCreate(); // will add the main bar view + + addPanelWindows(); + } + + protected View makeStatusBarView() { + Resources res = getResources(); + + mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); + + final View sb = View.inflate(this, R.layout.status_bar, null); + mStatusBarView = sb; + + // the more notifications icon + mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons); + + return sb; + } + + protected int getStatusBarGravity() { + return Gravity.BOTTOM | Gravity.FILL_HORIZONTAL; + } + + private class H extends Handler { + public static final int MSG_OPEN_NOTIFICATION_PANEL = 1000; + public static final int MSG_CLOSE_NOTIFICATION_PANEL = 1001; + public static final int MSG_OPEN_SYSTEM_PANEL = 1010; + public static final int MSG_CLOSE_SYSTEM_PANEL = 1011; + public void handleMessage(Message m) { + switch (m.what) { + case MSG_OPEN_NOTIFICATION_PANEL: + if (DEBUG) Slog.d(TAG, "opening notifications panel"); + mNotificationPanel.setVisibility(View.VISIBLE); + break; + case MSG_CLOSE_NOTIFICATION_PANEL: + if (DEBUG) Slog.d(TAG, "closing notifications panel"); + mNotificationPanel.setVisibility(View.GONE); + break; + case MSG_OPEN_SYSTEM_PANEL: + if (DEBUG) Slog.d(TAG, "opening system panel"); + mSystemPanel.setVisibility(View.VISIBLE); + break; + case MSG_CLOSE_SYSTEM_PANEL: + if (DEBUG) Slog.d(TAG, "closing system panel"); + mSystemPanel.setVisibility(View.GONE); + break; + } + } + } + + public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { + // TODO + } + + public void updateIcon(String slot, int index, int viewIndex, + StatusBarIcon old, StatusBarIcon icon) { + // TODO + } + + public void removeIcon(String slot, int index, int viewIndex) { + // TODO + } + + public void addNotification(IBinder key, StatusBarNotification notification) { + // TODO + } + + public void updateNotification(IBinder key, StatusBarNotification notification) { + // TODO + } + + public void removeNotification(IBinder key) { + // TODO + } + + public void disable(int state) { + // TODO + } + + public void animateExpand() { + mHandler.removeMessages(H.MSG_OPEN_NOTIFICATION_PANEL); + mHandler.sendEmptyMessage(H.MSG_OPEN_NOTIFICATION_PANEL); + } + + public void animateCollapse() { + mHandler.removeMessages(H.MSG_CLOSE_NOTIFICATION_PANEL); + mHandler.sendEmptyMessage(H.MSG_CLOSE_NOTIFICATION_PANEL); + mHandler.removeMessages(H.MSG_CLOSE_SYSTEM_PANEL); + mHandler.sendEmptyMessage(H.MSG_CLOSE_SYSTEM_PANEL); + } + + public void notificationIconsClicked(View v) { + if (DEBUG) Slog.d(TAG, "clicked notification icons"); + int msg = (mNotificationPanel.getVisibility() == View.GONE) + ? H.MSG_OPEN_NOTIFICATION_PANEL + : H.MSG_CLOSE_NOTIFICATION_PANEL; + mHandler.removeMessages(msg); + mHandler.sendEmptyMessage(msg); + } + + public void systemInfoClicked(View v) { + if (DEBUG) Slog.d(TAG, "clicked system info"); + int msg = (mSystemPanel.getVisibility() == View.GONE) + ? H.MSG_OPEN_SYSTEM_PANEL + : H.MSG_CLOSE_SYSTEM_PANEL; + mHandler.removeMessages(msg); + mHandler.sendEmptyMessage(msg); + } +} |