Intruder alert! (First try at the immersive-mode alert bar.)

When a FLAG_HIGH_PRIORITY notification is posted and the
foreground activity is immersive, this window will be shown
to the user. It disappears after a while (currently 10s,
which is far too long to be usable but is very handy for
testing) and can be dismissed by a tap.

Artwork is extremely rough; please ignore the aesthetics.

Still TODO:
  - sticky alerts for ongoing priority notifications
  - tap to launch PendingIntent associated with the
    notification

Change-Id: Ief4a98b84cc836d33359bd7d65de9909f5186317
This commit is contained in:
Daniel Sandler
2010-06-28 10:31:30 -04:00
parent de1057c4a6
commit 5dbd05ed18
4 changed files with 114 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -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:background="@drawable/alert_bar_background"
android:orientation="horizontal"
android:focusable="true"
android:descendantFocusability="afterDescendants"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="6dip"
android:animationCache="false"
android:orientation="horizontal"
>
<ImageView
android:id="@+id/alertIcon"
android:layout_width="25dip"
android:layout_height="25dip"
android:layout_marginRight="8dip"
/>
<TextView
android:id="@+id/alertText"
android:textAppearance="@*android:style/TextAppearance.StatusBar.EventContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
</LinearLayout>
</FrameLayout>

View File

@ -57,6 +57,7 @@ import android.view.WindowManager;
import android.view.WindowManagerImpl; import android.view.WindowManagerImpl;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.RemoteViews; import android.widget.RemoteViews;
import android.widget.ScrollView; import android.widget.ScrollView;
@ -84,6 +85,11 @@ public class PhoneStatusBarService extends StatusBarService {
private static final int MSG_ANIMATE = 1000; private static final int MSG_ANIMATE = 1000;
private static final int MSG_ANIMATE_REVEAL = 1001; 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;
private class ExpandedDialog extends Dialog { private class ExpandedDialog extends Dialog {
ExpandedDialog(Context context) { ExpandedDialog(Context context) {
@ -180,6 +186,9 @@ public class PhoneStatusBarService extends StatusBarService {
// for disabling the status bar // for disabling the status bar
int mDisabled = 0; int mDisabled = 0;
// for immersive activities
private View mIntruderAlertView;
/** /**
* Construct the service, add the status bar view to the window manager * Construct the service, add the status bar view to the window manager
*/ */
@ -208,6 +217,17 @@ public class PhoneStatusBarService extends StatusBarService {
ExpandedView expanded = (ExpandedView)View.inflate(context, ExpandedView expanded = (ExpandedView)View.inflate(context,
R.layout.status_bar_expanded, null); R.layout.status_bar_expanded, null);
expanded.mService = this; expanded.mService = this;
mIntruderAlertView = View.inflate(context, R.layout.intruder_alert, null);
mIntruderAlertView.setVisibility(View.GONE);
mIntruderAlertView.setClickable(true);
mIntruderAlertView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Slog.d(TAG, "Intruder Alert clicked!");
mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
}
});
StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null); StatusBarView sb = (StatusBarView)View.inflate(context, R.layout.status_bar, null);
sb.mService = this; sb.mService = this;
@ -286,6 +306,23 @@ public class PhoneStatusBarService extends StatusBarService {
// TODO lp.windowAnimations = R.style.Animation_StatusBar; // TODO lp.windowAnimations = R.style.Animation_StatusBar;
WindowManagerImpl.getDefault().addView(view, lp); WindowManagerImpl.getDefault().addView(view, lp);
lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
mHeight,
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 += mHeight * 1.5; // for now
lp.setTitle("IntruderAlert");
lp.windowAnimations = android.R.style.Animation_Dialog;
WindowManagerImpl.getDefault().addView(mIntruderAlertView, lp);
} }
public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
@ -310,7 +347,8 @@ public class PhoneStatusBarService extends StatusBarService {
} }
public void addNotification(IBinder key, StatusBarNotification notification) { public void addNotification(IBinder key, StatusBarNotification notification) {
addNotificationViews(key, notification); StatusBarIconView iconView = addNotificationViews(key, notification);
if (iconView == null) return;
boolean immersive = false; boolean immersive = false;
try { try {
@ -322,21 +360,22 @@ public class PhoneStatusBarService extends StatusBarService {
if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) { if ((notification.notification.flags & Notification.FLAG_HIGH_PRIORITY) != 0) {
Slog.d(TAG, "Presenting high-priority notification in immersive activity"); Slog.d(TAG, "Presenting high-priority notification in immersive activity");
// @@@ special new transient ticker mode // @@@ special new transient ticker mode
/* // 1. Populate mIntruderAlertView
// 1. Populate mAlertBarView
ImageView alertIcon = (ImageView) mAlertBarView.findViewById(R.id.alertIcon); ImageView alertIcon = (ImageView) mIntruderAlertView.findViewById(R.id.alertIcon);
TextView alertText = (TextView) mAlertBarView.findViewById(R.id.alertText); TextView alertText = (TextView) mIntruderAlertView.findViewById(R.id.alertText);
alertIcon.setImageDrawable(StatusBarIconView.getIcon( alertIcon.setImageDrawable(StatusBarIconView.getIcon(
alertIcon.getContext(), alertIcon.getContext(),
iconView.getStatusBarIcon())); iconView.getStatusBarIcon()));
alertText.setText(notification.notification.tickerText); alertText.setText(notification.notification.tickerText);
// 2. Animate mAlertBarView in // 2. Animate mIntruderAlertView in
mAlertBarView.setVisibility(View.VISIBLE); mHandler.removeMessages(MSG_HIDE_INTRUDER);
mHandler.sendEmptyMessage(MSG_SHOW_INTRUDER);
mHandler.sendEmptyMessageDelayed(MSG_HIDE_INTRUDER, INTRUDER_ALERT_DECAY_MS);
// 3. Set alarm to age the notification off (TODO) // 3. Set alarm to age the notification off (TODO)
*/
} }
} else if (notification.notification.fullScreenIntent != null) { } else if (notification.notification.fullScreenIntent != null) {
// not immersive & a full-screen alert should be shown // not immersive & a full-screen alert should be shown
@ -502,7 +541,7 @@ public class PhoneStatusBarService extends StatusBarService {
return new View[] { row, content, expanded }; return new View[] { row, content, expanded };
} }
void addNotificationViews(IBinder key, StatusBarNotification notification) { StatusBarIconView addNotificationViews(IBinder key, StatusBarNotification notification) {
NotificationData list; NotificationData list;
ViewGroup parent; ViewGroup parent;
final boolean isOngoing = notification.isOngoing(); final boolean isOngoing = notification.isOngoing();
@ -518,7 +557,7 @@ public class PhoneStatusBarService extends StatusBarService {
if (views == null) { if (views == null) {
handleNotificationError(key, notification, "Couldn't expand RemoteViews for: " handleNotificationError(key, notification, "Couldn't expand RemoteViews for: "
+ notification); + notification);
return; return null;
} }
final View row = views[0]; final View row = views[0];
final View content = views[1]; final View content = views[1];
@ -530,7 +569,7 @@ public class PhoneStatusBarService extends StatusBarService {
notification.notification.iconLevel, notification.notification.number); notification.notification.iconLevel, notification.notification.number);
if (!iconView.set(ic)) { if (!iconView.set(ic)) {
handleNotificationError(key, notification, "Coulding create icon: " + ic); handleNotificationError(key, notification, "Coulding create icon: " + ic);
return; return null;
} }
// Add the expanded view. // Add the expanded view.
final int viewIndex = list.add(key, notification, row, content, expanded, iconView); final int viewIndex = list.add(key, notification, row, content, expanded, iconView);
@ -539,6 +578,8 @@ public class PhoneStatusBarService extends StatusBarService {
final int iconIndex = chooseIconIndex(isOngoing, viewIndex); final int iconIndex = chooseIconIndex(isOngoing, viewIndex);
mNotificationIcons.addView(iconView, iconIndex, mNotificationIcons.addView(iconView, iconIndex,
new LinearLayout.LayoutParams(mIconWidth, mHeight)); new LinearLayout.LayoutParams(mIconWidth, mHeight));
return iconView;
} }
StatusBarNotification removeNotificationViews(IBinder key) { StatusBarNotification removeNotificationViews(IBinder key) {
@ -628,6 +669,12 @@ public class PhoneStatusBarService extends StatusBarService {
case MSG_ANIMATE_REVEAL: case MSG_ANIMATE_REVEAL:
doRevealAnimation(); doRevealAnimation();
break; break;
case MSG_SHOW_INTRUDER:
setIntruderAlertVisibility(true);
break;
case MSG_HIDE_INTRUDER:
setIntruderAlertVisibility(false);
break;
} }
} }
} }
@ -1440,6 +1487,10 @@ public class PhoneStatusBarService extends StatusBarService {
} }
}; };
private void setIntruderAlertVisibility(boolean vis) {
mIntruderAlertView.setVisibility(vis ? View.VISIBLE : View.GONE);
}
/** /**
* Reload some of our resources when the configuration changes. * Reload some of our resources when the configuration changes.
* *