*/
private static final boolean ENABLE_INSECURE_STATUS_BAR_EXPAND = true;
+ /**
+ * Allow the user to expand the status bar when a SECURE keyguard is engaged
+ * and {@link Settings.Secure#LOCK_SCREEN_ALLOW_NOTIFICATIONS} is set
+ * (private notifications will be masked).
+ */
+ private static final boolean ENABLE_SECURE_STATUS_BAR_EXPAND = true;
+
+ /**
+ * Default value of {@link Settings.Secure#LOCK_SCREEN_ALLOW_NOTIFICATIONS}.
+ */
+ private static final boolean ALLOW_NOTIFICATIONS_DEFAULT = false;
+
/** The stream type that the lock sounds are tied to. */
private int mMasterStreamType;
private int mLockSoundStreamId;
/**
+ * Tracks value of {@link Settings.Secure#LOCK_SCREEN_ALLOW_NOTIFICATIONS}.
+ */
+ private boolean mAllowNotificationsWhenSecure;
+
+ /**
* The volume applied to the lock/unlock sounds.
*/
private final float mLockSoundVolume;
return;
}
+ // note whether notification access should be allowed
+ mAllowNotificationsWhenSecure = ENABLE_SECURE_STATUS_BAR_EXPAND
+ && 0 != Settings.Secure.getInt(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCK_SCREEN_ALLOW_NOTIFICATIONS,
+ ALLOW_NOTIFICATIONS_DEFAULT ? 1 : 0);
+
// if the keyguard is already showing, don't bother
if (mKeyguardViewManager.isShowing()) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
// (like recents). Temporary enable/disable (e.g. the "back" button) are
// done in KeyguardHostView.
flags |= StatusBarManager.DISABLE_RECENT;
- if (isSecure() || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
+ if ((isSecure() && !mAllowNotificationsWhenSecure)
+ || !ENABLE_INSECURE_STATUS_BAR_EXPAND) {
// showing secure lockscreen; disable expanding.
flags |= StatusBarManager.DISABLE_EXPAND;
}
if (isSecure()) {
- // showing secure lockscreen; disable ticker.
- flags |= StatusBarManager.DISABLE_NOTIFICATION_TICKER;
+ // showing secure lockscreen; disable ticker and switch private notifications
+ // to show their public versions, if available.
+ flags |= StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS;
}
if (!isAssistantAvailable()) {
flags |= StatusBarManager.DISABLE_SEARCH;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Configuration;
import android.database.ContentObserver;
protected IDreamManager mDreamManager;
PowerManager mPowerManager;
protected int mRowHeight;
+ private boolean mPublicMode = false;
// UI-specific methods
public abstract void resetHeadsUpDecayTimer();
+ public void setPublicMode(boolean publicMode) {
+ mPublicMode = publicMode;
+ }
+
+ public boolean isPublicMode() {
+ return mPublicMode;
+ }
+
protected class H extends Handler {
public void handleMessage(Message m) {
Intent intent;
return false;
}
+ Log.v(TAG, "publicNotification: "
+ + sbn.getNotification().publicVersion);
+
+ Notification publicNotification = sbn.getNotification().publicVersion;
+
// create the row view
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
// NB: the large icon is now handled entirely by the template
// bind the click event to the content area
- ViewGroup content = (ViewGroup)row.findViewById(R.id.content);
- ViewGroup adaptive = (ViewGroup)row.findViewById(R.id.adaptive);
+ ViewGroup content = (ViewGroup)row.findViewById(R.id.container);
+ SizeAdaptiveLayout expanded = (SizeAdaptiveLayout)row.findViewById(R.id.expanded);
+ SizeAdaptiveLayout expandedPublic
+ = (SizeAdaptiveLayout)row.findViewById(R.id.expandedPublic);
content.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
content.setOnClickListener(null);
}
+ // set up the adaptive layout
View contentViewLocal = null;
View bigContentViewLocal = null;
try {
- contentViewLocal = contentView.apply(mContext, adaptive, mOnClickHandler);
+ contentViewLocal = contentView.apply(mContext, expanded, mOnClickHandler);
if (bigContentView != null) {
- bigContentViewLocal = bigContentView.apply(mContext, adaptive, mOnClickHandler);
+ bigContentViewLocal = bigContentView.apply(mContext, expanded, mOnClickHandler);
}
}
catch (RuntimeException e) {
new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
params.minHeight = minHeight;
params.maxHeight = minHeight;
- adaptive.addView(contentViewLocal, params);
+ expanded.addView(contentViewLocal, params);
}
if (bigContentViewLocal != null) {
SizeAdaptiveLayout.LayoutParams params =
new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
params.minHeight = minHeight+1;
params.maxHeight = maxHeight;
- adaptive.addView(bigContentViewLocal, params);
+ expanded.addView(bigContentViewLocal, params);
+ }
+
+ PackageManager pm = mContext.getPackageManager();
+
+ // now the public version
+ View publicViewLocal = null;
+ if (publicNotification != null) {
+ try {
+ publicViewLocal = publicNotification.contentView.apply(mContext,
+ expandedPublic, mOnClickHandler);
+
+ if (publicViewLocal != null) {
+ SizeAdaptiveLayout.LayoutParams params =
+ new SizeAdaptiveLayout.LayoutParams(publicViewLocal.getLayoutParams());
+ params.minHeight = minHeight;
+ params.maxHeight = minHeight;
+ expandedPublic.addView(publicViewLocal, params);
+ }
+ }
+ catch (RuntimeException e) {
+ final String ident = sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId());
+ Log.e(TAG, "couldn't inflate public view for notification " + ident, e);
+ publicViewLocal = null;
+ }
}
+
+ if (publicViewLocal == null) {
+ // Add a basic notification template
+ publicViewLocal = LayoutInflater.from(mContext).inflate(
+ com.android.internal.R.layout.notification_template_base, expandedPublic, true);
+
+ final TextView title = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.title);
+ try {
+ title.setText(pm.getApplicationLabel(
+ pm.getApplicationInfo(entry.notification.getPackageName(), 0)));
+ } catch (NameNotFoundException e) {
+ title.setText(entry.notification.getPackageName());
+ }
+
+ final ImageView icon = (ImageView) publicViewLocal.findViewById(com.android.internal.R.id.icon);
+
+ final StatusBarIcon ic = new StatusBarIcon(entry.notification.getPackageName(),
+ entry.notification.getUser(),
+ entry.notification.getNotification().icon,
+ entry.notification.getNotification().iconLevel,
+ entry.notification.getNotification().number,
+ entry.notification.getNotification().tickerText);
+
+ icon.setImageDrawable(StatusBarIconView.getIcon(mContext, ic));
+
+ final TextView text = (TextView) publicViewLocal.findViewById(com.android.internal.R.id.text);
+ text.setText("Unlock your device to see this notification.");
+
+ // TODO: fill out "time" as well
+ }
+
row.setDrawingCacheEnabled(true);
applyLegacyRowBackground(sbn, content);
entry.row.setRowHeight(mRowHeight);
entry.content = content;
entry.expanded = contentViewLocal;
+ entry.expandedPublic = publicViewLocal;
entry.setBigContentView(bigContentViewLocal);
return true;
final RemoteViews contentView = notification.getNotification().contentView;
final RemoteViews oldBigContentView = oldNotification.getNotification().bigContentView;
final RemoteViews bigContentView = notification.getNotification().bigContentView;
+ final Notification oldPublicNotification = oldNotification.getNotification().publicVersion;
+ final RemoteViews oldPublicContentView = oldPublicNotification != null
+ ? oldPublicNotification.contentView : null;
+ final Notification publicNotification = notification.getNotification().publicVersion;
+ final RemoteViews publicContentView = publicNotification != null
+ ? publicNotification.contentView : null;
if (DEBUG) {
Log.d(TAG, "old notification: when=" + oldNotification.getNotification().when
+ " expanded=" + oldEntry.expanded
+ " contentView=" + oldContentView
+ " bigContentView=" + oldBigContentView
+ + " publicView=" + oldPublicContentView
+ " rowParent=" + oldEntry.row.getParent());
Log.d(TAG, "new notification: when=" + notification.getNotification().when
+ " ongoing=" + oldNotification.isOngoing()
+ " contentView=" + contentView
- + " bigContentView=" + bigContentView);
+ + " bigContentView=" + bigContentView
+ + " publicView=" + publicContentView);
}
// Can we just reapply the RemoteViews in place? If when didn't change, the order
&& oldBigContentView.getPackage() != null
&& oldBigContentView.getPackage().equals(bigContentView.getPackage())
&& oldBigContentView.getLayoutId() == bigContentView.getLayoutId());
+ boolean publicUnchanged =
+ (oldPublicContentView == null && publicContentView == null)
+ || ((oldPublicContentView != null && publicContentView != null)
+ && publicContentView.getPackage() != null
+ && oldPublicContentView.getPackage() != null
+ && oldPublicContentView.getPackage().equals(publicContentView.getPackage())
+ && oldPublicContentView.getLayoutId() == publicContentView.getLayoutId());
+
ViewGroup rowParent = (ViewGroup) oldEntry.row.getParent();
- boolean orderUnchanged = notification.getNotification().when== oldNotification.getNotification().when
+ boolean orderUnchanged =
+ notification.getNotification().when == oldNotification.getNotification().when
&& notification.getScore() == oldNotification.getScore();
// score now encompasses/supersedes isOngoing()
&& !TextUtils.equals(notification.getNotification().tickerText,
oldEntry.notification.getNotification().tickerText);
boolean isTopAnyway = isTopNotification(rowParent, oldEntry);
- if (contentsUnchanged && bigContentsUnchanged && (orderUnchanged || isTopAnyway)) {
+ if (contentsUnchanged && bigContentsUnchanged && publicUnchanged
+ && (orderUnchanged || isTopAnyway)) {
if (DEBUG) Log.d(TAG, "reusing notification for key: " + key);
oldEntry.notification = notification;
try {
StatusBarNotification notification) {
final RemoteViews contentView = notification.getNotification().contentView;
final RemoteViews bigContentView = notification.getNotification().bigContentView;
+ final RemoteViews publicContentView
+ = notification.getNotification().publicVersion.contentView;
+
// Reapply the RemoteViews
contentView.reapply(mContext, entry.expanded, mOnClickHandler);
if (bigContentView != null && entry.getBigContentView() != null) {
bigContentView.reapply(mContext, entry.getBigContentView(), mOnClickHandler);
}
+ if (publicContentView != null && entry.getPublicContentView() != null) {
+ publicContentView.reapply(mContext, entry.getPublicContentView(), mOnClickHandler);
+ }
// update the contentIntent
final PendingIntent contentIntent = notification.getNotification().contentIntent;
if (contentIntent != null) {
mScrollView.setVerticalScrollBarEnabled(false); // less drawing during pulldowns
if (!mNotificationPanelIsFullScreenWidth) {
mScrollView.setSystemUiVisibility(
- View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER |
View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS |
View.STATUS_BAR_DISABLE_CLOCK);
}
mQS = new QuickSettings(mContext, mSettingsContainer);
if (!mNotificationPanelIsFullScreenWidth) {
mSettingsContainer.setSystemUiVisibility(
- View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
+ View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
| View.STATUS_BAR_DISABLE_SYSTEM_INFO);
}
if (mSettingsPanel != null) {
Entry ent = mNotificationData.get(N-i-1);
if (!(provisioned || showNotificationEvenIfUnprovisioned(ent.notification))) continue;
if (!notificationIsForCurrentUser(ent.notification)) continue;
- toShow.add(ent.row);
+ final int vis = ent.notification.getNotification().visibility;
+ if (vis != Notification.VISIBILITY_SECRET) {
+ // when isPublicMode() we show the public form of VISIBILITY_PRIVATE notifications
+ ent.row.setShowingPublic(isPublicMode() && vis == Notification.VISIBILITY_PRIVATE);
+ toShow.add(ent.row);
+ }
}
ArrayList<View> toRemove = new ArrayList<View>();
if (!((provisioned && ent.notification.getScore() >= HIDE_ICONS_BELOW_SCORE)
|| showNotificationEvenIfUnprovisioned(ent.notification))) continue;
if (!notificationIsForCurrentUser(ent.notification)) continue;
+ if (isPublicMode()
+ && ent.notification.getNotification().visibility
+ == Notification.VISIBILITY_SECRET) {
+ // in "public" mode (atop a secure keyguard), secret notifs are totally hidden
+ continue;
+ }
toShow.add(ent.icon);
}
flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) ? "* " : " ");
flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "ALERTS" : "alerts");
flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) ? "* " : " ");
- flagdbg.append(((state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "TICKER" : "ticker");
- flagdbg.append(((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) ? "* " : " ");
+ flagdbg.append(((state & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) ? "PRIVATE" : "private");
+ flagdbg.append(((diff & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) ? "* " : " ");
flagdbg.append(((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "SYSTEM_INFO" : "system_info");
flagdbg.append(((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) ? "* " : " ");
flagdbg.append(((state & StatusBarManager.DISABLE_BACK) != 0) ? "BACK" : "back");
.setDuration(175)
.start();
}
- } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- if (mTicking && (state & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
- haltTicker();
+ } else if ((diff & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) {
+ if ((state & StatusBarManager.DISABLE_PRIVATE_NOTIFICATIONS) != 0) {
+ // we are outside a secure keyguard, so we need to switch to "public" mode
+ setPublicMode(true);
+ } else {
+ // user has authenticated the device; full notifications may be shown
+ setPublicMode(false);
}
+ updateNotificationIcons();
}
}