Merge "Introduce zen mode."

This commit is contained in:
John Spurlock
2014-02-27 17:29:54 +00:00
committed by Android (Google) Code Review
25 changed files with 1239 additions and 71 deletions

View File

@@ -6081,6 +6081,24 @@ public final class Settings {
public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
"lock_screen_show_notifications";
/**
* Defines global zen mode. One of ZEN_MODE_OFF, ZEN_MODE_LIMITED, ZEN_MODE_FULL.
*
* @hide
*/
public static final String ZEN_MODE = "zen_mode";
/** @hide */ public static final int ZEN_MODE_OFF = 0;
/** @hide */ public static final int ZEN_MODE_LIMITED = 1;
/** @hide */ public static final int ZEN_MODE_FULL = 2;
/** @hide */ public static String zenModeToString(int mode) {
if (mode == ZEN_MODE_OFF) return "ZEN_MODE_OFF";
if (mode == ZEN_MODE_LIMITED) return "ZEN_MODE_LIMITED";
if (mode == ZEN_MODE_FULL) return "ZEN_MODE_FULL";
throw new IllegalArgumentException("Invalid zen mode: " + mode);
}
/**
* Settings to backup. This is here so that it's in the same place as the settings
* keys and easy to update.

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 745 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 614 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 989 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -62,7 +62,11 @@
android:src="@drawable/stat_notify_more"
android:visibility="gone"
/>
<com.android.systemui.statusbar.StatusBarIconView android:id="@+id/modeIcon"
android:layout_width="@dimen/status_bar_icon_size"
android:layout_height="match_parent"
android:visibility="gone"
/>
<com.android.systemui.statusbar.phone.IconMerger android:id="@+id/notificationIcons"
android:layout_width="match_parent"
android:layout_height="match_parent"

View File

@@ -58,6 +58,12 @@
android:layout_height="@dimen/notification_panel_header_height"
/>
<com.android.systemui.statusbar.phone.ZenModeView
android:id="@+id/zenmode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:id="@+id/emergency_calls_only"
android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Network.EmergencyOnly"

View File

@@ -15,7 +15,7 @@
** limitations under the License.
-->
<LinearLayout
<com.android.systemui.statusbar.phone.PanelHeaderView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:id="@+id/header"
@@ -106,4 +106,4 @@
android:contentDescription="@string/accessibility_notifications_button"
/>
</FrameLayout>
</LinearLayout>
</com.android.systemui.statusbar.phone.PanelHeaderView>

View File

@@ -104,6 +104,9 @@ public abstract class BaseStatusBar extends SystemUI implements
public static final int EXPANDED_LEAVE_ALONE = -10000;
public static final int EXPANDED_FULL_OPEN = -10001;
private static final String EXTRA_INTERCEPT = "android.intercept";
private static final float INTERCEPTED_ALPHA = .2f;
protected CommandQueue mCommandQueue;
protected IStatusBarService mBarService;
protected H mHandler = createHandler();
@@ -155,6 +158,8 @@ public abstract class BaseStatusBar extends SystemUI implements
private RecentsComponent mRecents;
protected int mZenMode;
public IStatusBarService getStatusBarService() {
return mBarService;
}
@@ -163,7 +168,7 @@ public abstract class BaseStatusBar extends SystemUI implements
return mDeviceProvisioned;
}
private final ContentObserver mProvisioningObserver = new ContentObserver(mHandler) {
protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
final boolean provisioned = 0 != Settings.Global.getInt(
@@ -172,6 +177,9 @@ public abstract class BaseStatusBar extends SystemUI implements
mDeviceProvisioned = provisioned;
updateNotificationIcons();
}
final int mode = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
setZenMode(mode);
}
};
@@ -239,10 +247,13 @@ public abstract class BaseStatusBar extends SystemUI implements
ServiceManager.checkService(DreamService.DREAM_SERVICE));
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mProvisioningObserver.onChange(false); // set up
mSettingsObserver.onChange(false); // set up
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true,
mProvisioningObserver);
mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
mSettingsObserver);
mContext.getContentResolver().registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
@@ -980,6 +991,7 @@ public abstract class BaseStatusBar extends SystemUI implements
if (DEBUG) {
Log.d(TAG, "addNotificationViews: added at " + pos);
}
updateInterceptedState(entry);
updateExpansionStates();
updateNotificationIcons();
}
@@ -1010,6 +1022,34 @@ public abstract class BaseStatusBar extends SystemUI implements
}
}
protected void setZenMode(int mode) {
final boolean change = mZenMode != mode;
mZenMode = mode;
final int N = mNotificationData.size();
for (int i = 0; i < N; i++) {
final NotificationData.Entry entry = mNotificationData.get(i);
if (change && !shouldIntercept()) {
entry.notification.getNotification().extras.putBoolean(EXTRA_INTERCEPT, false);
}
updateInterceptedState(entry);
}
updateNotificationIcons();
}
private boolean shouldIntercept() {
return mZenMode == Settings.Global.ZEN_MODE_LIMITED
|| mZenMode == Settings.Global.ZEN_MODE_FULL;
}
protected boolean shouldIntercept(Notification n) {
return shouldIntercept() && n.extras.getBoolean(EXTRA_INTERCEPT);
}
private void updateInterceptedState(NotificationData.Entry entry) {
final boolean intercepted = shouldIntercept(entry.notification.getNotification());
entry.row.findViewById(R.id.container).setAlpha(intercepted ? INTERCEPTED_ALPHA : 1);
}
protected abstract void haltTicker();
protected abstract void setAreThereNotifications();
protected abstract void updateNotificationIcons();
@@ -1202,6 +1242,7 @@ public abstract class BaseStatusBar extends SystemUI implements
} else {
entry.content.setOnClickListener(null);
}
updateInterceptedState(entry);
}
protected void notifyHeadsUpScreenOn(boolean screenOn) {

View File

@@ -56,6 +56,10 @@ public class NotificationPanelView extends PanelView {
mHandleBar = resources.getDrawable(R.drawable.status_bar_close);
mHandleBarHeight = resources.getDimensionPixelSize(R.dimen.close_handle_height);
mHandleView = findViewById(R.id.handle);
PanelHeaderView header = (PanelHeaderView) findViewById(R.id.header);
ZenModeView zenModeView = (ZenModeView) findViewById(R.id.zenmode);
zenModeView.setAdapter( new ZenModeViewAdapter(mContext));
header.setZenModeView(zenModeView);
}
@Override

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2014 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.phone;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
public class PanelHeaderView extends LinearLayout {
private static final String TAG = "PanelHeaderView";
private static final boolean DEBUG = false;
private ZenModeView mZenModeView;
public PanelHeaderView(Context context) {
super(context);
}
public PanelHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setZenModeView(ZenModeView zmv) {
mZenModeView = zmv;
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean rt = super.dispatchTouchEvent(ev);
if (DEBUG) logTouchEvent("dispatchTouchEvent", rt, ev);
if (mZenModeView != null) {
mZenModeView.dispatchExternalTouchEvent(ev);
}
return rt;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final boolean rt = super.onInterceptTouchEvent(ev);
if (DEBUG) logTouchEvent("onInterceptTouchEvent", rt, ev);
return rt;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean rt = super.onTouchEvent(event);
if (DEBUG) logTouchEvent("onTouchEvent", rt, event);
return true;
}
private void logTouchEvent(String method, boolean rt, MotionEvent ev) {
Log.d(TAG, method + " " + (rt ? "TRUE" : "FALSE") + " " + ev);
}
}

View File

@@ -189,6 +189,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
IconMerger mNotificationIcons;
// [+>
View mMoreIcon;
// mode indicator icon
ImageView mModeIcon;
// expanded notifications
NotificationPanelView mNotificationPanel; // the sliding/resizing panel within the notification window
@@ -340,6 +342,19 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
}
}};
@Override
public void setZenMode(int mode) {
super.setZenMode(mode);
if (mModeIcon == null) return;
final boolean limited = mode == Settings.Global.ZEN_MODE_LIMITED;
final boolean full = mode == Settings.Global.ZEN_MODE_FULL;
mModeIcon.setVisibility(full || limited ? View.VISIBLE : View.GONE);
final int icon = limited ? R.drawable.stat_sys_zen_limited : R.drawable.stat_sys_zen_full;
if (full || limited) {
mModeIcon.setImageResource(icon);
}
}
@Override
public void start() {
mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
@@ -352,6 +367,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
// Lastly, call to the icon policy to install/update all the icons.
mIconPolicy = new PhoneStatusBarPolicy(mContext);
mSettingsObserver.onChange(false); // set up
mHeadsUpObserver.onChange(true); // set up
if (ENABLE_HEADS_UP) {
@@ -455,6 +471,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
mNotificationIcons.setOverflowIndicator(mMoreIcon);
mModeIcon = (ImageView)mStatusBarView.findViewById(R.id.modeIcon);
mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
mTickerView = mStatusBarView.findViewById(R.id.ticker);
@@ -908,41 +925,43 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
if (shadeEntry == null) {
return;
}
if (mUseHeadsUp && shouldInterrupt(notification)) {
if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
Entry interruptionCandidate = new Entry(key, notification, null);
ViewGroup holder = mHeadsUpNotificationView.getHolder();
if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
mInterruptingNotificationTime = System.currentTimeMillis();
mInterruptingNotificationEntry = interruptionCandidate;
shadeEntry.setInterruption();
if (!shouldIntercept(notification.getNotification())) {
if (mUseHeadsUp && shouldInterrupt(notification)) {
if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
Entry interruptionCandidate = new Entry(key, notification, null);
ViewGroup holder = mHeadsUpNotificationView.getHolder();
if (inflateViewsForHeadsUp(interruptionCandidate, holder)) {
mInterruptingNotificationTime = System.currentTimeMillis();
mInterruptingNotificationEntry = interruptionCandidate;
shadeEntry.setInterruption();
// 1. Populate mHeadsUpNotificationView
mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
// 1. Populate mHeadsUpNotificationView
mHeadsUpNotificationView.setNotification(mInterruptingNotificationEntry);
// 2. Animate mHeadsUpNotificationView in
mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
// 2. Animate mHeadsUpNotificationView in
mHandler.sendEmptyMessage(MSG_SHOW_HEADS_UP);
// 3. Set alarm to age the notification off
resetHeadsUpDecayTimer();
}
} else if (notification.getNotification().fullScreenIntent != null) {
// Stop screensaver if the notification has a full-screen intent.
// (like an incoming phone call)
awakenDreams();
// 3. Set alarm to age the notification off
resetHeadsUpDecayTimer();
}
} else if (notification.getNotification().fullScreenIntent != null) {
// Stop screensaver if the notification has a full-screen intent.
// (like an incoming phone call)
awakenDreams();
// not immersive & a full-screen alert should be shown
if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
notification.getNotification().fullScreenIntent.send();
} catch (PendingIntent.CanceledException e) {
}
} else {
// usual case: status bar visible & not immersive
// not immersive & a full-screen alert should be shown
if (DEBUG) Log.d(TAG, "Notification has fullScreenIntent; sending fullScreenIntent");
try {
notification.getNotification().fullScreenIntent.send();
} catch (PendingIntent.CanceledException e) {
}
} else {
// usual case: status bar visible & not immersive
// show the ticker if there isn't already a heads up
if (mInterruptingNotificationEntry == null) {
tick(null, notification, true);
// show the ticker if there isn't already a heads up
if (mInterruptingNotificationEntry == null) {
tick(null, notification, true);
}
}
}
addNotificationViews(shadeEntry);
@@ -1095,6 +1114,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
// in "public" mode (atop a secure keyguard), secret notifs are totally hidden
continue;
}
if (shouldIntercept(ent.notification.getNotification())) {
continue;
}
toShow.add(ent.icon);
}
@@ -2182,6 +2204,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
pw.println(windowStateToString(mStatusBarWindowState));
pw.print(" mStatusBarMode=");
pw.println(BarTransitions.modeToString(mStatusBarMode));
pw.print(" mZenMode=");
pw.println(Settings.Global.zenModeToString(mZenMode));
dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
if (mNavigationBarView != null) {
pw.print(" mNavigationBarWindowState=");

View File

@@ -0,0 +1,764 @@
/*
* Copyright (C) 2014 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.phone;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.TextPaint;
import android.text.method.LinkMovementMethod;
import android.text.style.RelativeSizeSpan;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.ZenModeView.Adapter.ExitCondition;
public class ZenModeView extends RelativeLayout {
private static final String TAG = ZenModeView.class.getSimpleName();
private static final boolean DEBUG = false;
private static final Typeface CONDENSED =
Typeface.create("sans-serif-condensed", Typeface.NORMAL);
private static final int GRAY = 0xff999999; //TextAppearance.StatusBar.Expanded.Network
private static final int BACKGROUND = 0xff1d3741; //0x3333b5e5;
private static final long DURATION = new ValueAnimator().getDuration();
private static final long BOUNCE_DURATION = DURATION / 3;
private static final float BOUNCE_SCALE = 0.8f;
private static final float SETTINGS_ALPHA = 0.6f;
private static final int INFO_WINDOW_DELAY = 2000;
private static final String LIMITED_TEXT =
"New notifications suppressed except calls, alarms & timers.";
private static final String FULL_TEXT =
"You won't hear any calls, alarms or timers.";
private final Context mContext;
private final Paint mPathPaint;
private final TextView mHintText;
private final ModeSpinner mModeSpinner;
private final ImageView mCloseButton;
private final ImageView mSettingsButton;
private final InfoWindow mInfoWindow;
private final Rect mLayoutRect = new Rect();
private final UntilPager mUntilPager;
private final AlarmWarning mAlarmWarning;
private float mDownY;
private int mDownBottom;
private boolean mPeekable = true;
private boolean mClosing;
private int mBottom;
private int mWidthSpec;
private Adapter mAdapter;
public ZenModeView(Context context) {
this(context, null);
}
public ZenModeView(Context context, AttributeSet attrs) {
super(context, attrs);
if (DEBUG) log("new %s()", getClass().getSimpleName());
mContext = context;
mPathPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPathPaint.setStyle(Paint.Style.STROKE);
mPathPaint.setColor(GRAY);
mPathPaint.setStrokeWidth(5);
final int iconSize = mContext.getResources()
.getDimensionPixelSize(com.android.internal.R.dimen.notification_large_icon_width);
final int topRowSize = iconSize * 2 / 3;
mCloseButton = new ImageView(mContext);
mCloseButton.setAlpha(0f);
mCloseButton.setImageDrawable(sd(closePath(topRowSize), topRowSize, mPathPaint));
addView(mCloseButton, new LayoutParams(topRowSize, topRowSize));
mCloseButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bounce(v, null);
close();
}
});
mSettingsButton = new ImageView(mContext);
mSettingsButton.setAlpha(0f);
final int p = topRowSize / 7;
mSettingsButton.setPadding(p, p, p, p);
mSettingsButton.setImageResource(R.drawable.ic_notify_settings_normal);
LayoutParams lp = new LayoutParams(topRowSize, topRowSize);
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
addView(mSettingsButton, lp);
mSettingsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAdapter == null || mAdapter.getMode() != Adapter.MODE_LIMITED) {
return;
}
if (!mInfoWindow.isShowing()) {
mInfoWindow.show(mUntilPager);
}
bounce(mSettingsButton, null);
}
});
mInfoWindow = new InfoWindow(mContext, LIMITED_TEXT);
mModeSpinner = new ModeSpinner(mContext);
mModeSpinner.setAlpha(0);
mModeSpinner.setEnabled(false);
mModeSpinner.setId(android.R.id.title);
lp = new LayoutParams(LayoutParams.WRAP_CONTENT, topRowSize);
lp.addRule(RelativeLayout.CENTER_HORIZONTAL);
addView(mModeSpinner, lp);
mUntilPager = new UntilPager(mContext, mPathPaint, iconSize);
mUntilPager.setId(android.R.id.tabhost);
mUntilPager.setAlpha(0);
lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(BELOW, mModeSpinner.getId());
addView(mUntilPager, lp);
mAlarmWarning = new AlarmWarning(mContext);
mAlarmWarning.setAlpha(0);
lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(CENTER_HORIZONTAL);
lp.addRule(BELOW, mUntilPager.getId());
addView(mAlarmWarning, lp);
mHintText = new TextView(mContext);
mHintText.setTypeface(CONDENSED);
mHintText.setText("Swipe down for Limited Interruptions");
mHintText.setGravity(Gravity.CENTER);
mHintText.setTextColor(GRAY);
addView(mHintText, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
}
private void close() {
mClosing = true;
final int startBottom = mBottom;
final int max = mPeekable ? getExpandedBottom() : startBottom;
mHintText.animate().alpha(1).setUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final float f = animation.getAnimatedFraction();
final int hintBottom = mHintText.getBottom();
setPeeked(hintBottom + (int)((1-f) * (startBottom - hintBottom)), max);
if (f == 1) {
mPeekable = true;
mClosing = false;
mModeSpinner.updateState();
if (mAdapter != null) {
mAdapter.cancel();
}
}
}
}).start();
mUntilPager.animate().alpha(0).start();
mAlarmWarning.animate().alpha(0).start();
mInfoWindow.dismiss();
}
public void setAdapter(Adapter adapter) {
mAdapter = adapter;
mAdapter.setCallbacks(new Adapter.Callbacks() {
@Override
public void onChanged() {
post(new Runnable() {
@Override
public void run() {
updateState(true);
}
});
}
});
updateState(false);
}
private void updateState(boolean animate) {
if (mAdapter != null && mAdapter.getMode() == Adapter.MODE_OFF && !mPeekable) {
close();
} else {
mModeSpinner.updateState();
mUntilPager.updateState();
mAlarmWarning.updateState(animate);
final float settingsAlpha = getSettingsButtonAlpha();
if (settingsAlpha != mSettingsButton.getAlpha()) {
if (animate) {
mSettingsButton.animate().alpha(settingsAlpha).start();
} else {
mSettingsButton.setAlpha(settingsAlpha);
}
}
if (mPeekable && mAdapter != null && mAdapter.getMode() != Adapter.MODE_OFF) {
if (DEBUG) log("panic expand!");
mPeekable = false;
mModeSpinner.setEnabled(true);
mBottom = getExpandedBottom();
setExpanded(1);
}
mInfoWindow.dismiss();
}
}
private float getSettingsButtonAlpha() {
final boolean full = mAdapter != null && mAdapter.getMode() == Adapter.MODE_FULL;
final boolean collapsed = mHintText.getAlpha() == 1;
return full || collapsed ? 0 : SETTINGS_ALPHA;
}
private static Path closePath(int size) {
final int pad = size / 4;
final Path p = new Path();
p.moveTo(pad, pad);
p.lineTo(size - pad, size - pad);
p.moveTo(size - pad, pad);
p.lineTo(pad, size - pad);
return p;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (DEBUG) log("onMeasure %s %s",
MeasureSpec.toString(widthMeasureSpec), MeasureSpec.toString(heightMeasureSpec));
if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
throw new UnsupportedOperationException("Width must be exact");
}
if (widthMeasureSpec != mWidthSpec) {
if (DEBUG) log(" super.onMeasure");
final int hms = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
super.onMeasure(widthMeasureSpec, hms);
mBottom = mPeekable ? mHintText.getMeasuredHeight() : getExpandedBottom();
mWidthSpec = widthMeasureSpec;
}
if (DEBUG) log("mBottom (OM) = " + mBottom);
setMeasuredDimension(getMeasuredWidth(), mBottom);
if (DEBUG) log(" mw=%s mh=%s",
toString(getMeasuredWidthAndState()), toString(getMeasuredHeightAndState()));
}
private static String toString(int sizeAndState) {
final int size = sizeAndState & MEASURED_SIZE_MASK;
final boolean tooSmall = (sizeAndState & MEASURED_STATE_TOO_SMALL) != 0;
return size + (tooSmall ? "TOO SMALL" : "");
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mLayoutRect.set(left, top, right, bottom);
if (DEBUG) log("onLayout %s %s %dx%d", changed,
mLayoutRect.toShortString(), mLayoutRect.width(), mLayoutRect.height());
super.onLayout(changed, left, top, right, bottom);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
final boolean rt = super.dispatchTouchEvent(ev);
if (DEBUG) logTouchEvent("dispatchTouchEvent", rt, ev);
return rt;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final boolean rt = super.onInterceptTouchEvent(ev);
if (DEBUG) logTouchEvent("onInterceptTouchEvent", rt, ev);
if (ev.getAction() == MotionEvent.ACTION_DOWN
&& ev.getY() > mCloseButton.getBottom()
&& mPeekable) {
return true;
}
return rt;
}
private static void logTouchEvent(String method, boolean rt, MotionEvent event) {
final String action = MotionEvent.actionToString(event.getAction());
Log.d(TAG, method + " " + (rt ? "TRUE" : "FALSE") + " " + action);
}
private int getExpandedBottom() {
int b = mModeSpinner.getMeasuredHeight() + mUntilPager.getMeasuredHeight();
if (mAlarmWarning.getAlpha() == 1) b += mAlarmWarning.getMeasuredHeight();
return b;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean rt = super.onTouchEvent(event);
if (DEBUG) logTouchEvent("onTouchEvent", rt, event);
if (!mPeekable) {
return rt;
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mDownY = event.getY();
if (DEBUG) log(" mDownY=" + mDownY);
mDownBottom = mBottom;
return true;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
final float dy = event.getY() - mDownY;
setPeeked(mDownBottom + (int)dy, getExpandedBottom());
} else if (event.getAction() == MotionEvent.ACTION_UP
|| event.getAction() == MotionEvent.ACTION_CANCEL) {
final float dy = event.getY() - mDownY;
setPeeked(mDownBottom + (int)dy, getExpandedBottom());
if (mPeekable) {
close();
}
}
return rt;
}
private void setPeeked(int peeked, int max) {
if (DEBUG) log("setPeeked=" + peeked);
final int min = mHintText.getBottom();
peeked = Math.max(min, Math.min(peeked, max));
if (mBottom == peeked) {
return;
}
if (peeked == max) {
mPeekable = false;
mModeSpinner.setEnabled(true);
if (mAdapter != null) {
mAdapter.setMode(Adapter.MODE_LIMITED);
}
}
if (peeked == min) {
mPeekable = true;
mModeSpinner.setEnabled(false);
}
if (DEBUG) log(" mBottom=" + peeked);
mBottom = peeked;
final float f = (peeked - min) / (float)(max - min);
setExpanded(f);
requestLayout();
}
private void setExpanded(float f) {
if (DEBUG) log("setExpanded " + f);
final int a = (int)(Color.alpha(BACKGROUND) * f);
setBackgroundColor(Color.argb(a,
Color.red(BACKGROUND), Color.green(BACKGROUND), Color.blue(BACKGROUND)));
mHintText.setAlpha(1 - f);
mCloseButton.setAlpha(f);
mModeSpinner.setAlpha(f);
mUntilPager.setAlpha(f);
mSettingsButton.setAlpha(f * getSettingsButtonAlpha());
}
private static void log(String msg, Object... args) {
Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args));
}
private static ShapeDrawable sd(Path p, int size, Paint pt) {
final ShapeDrawable sd = new ShapeDrawable(new PathShape(p, size, size));
sd.getPaint().set(pt);
sd.setIntrinsicHeight(size);
sd.setIntrinsicWidth(size);
return sd;
}
public void dispatchExternalTouchEvent(MotionEvent ev) {
onTouchEvent(ev);
}
private static void bounce(final View v, final Runnable midBounce) {
v.animate().scaleX(BOUNCE_SCALE).scaleY(BOUNCE_SCALE).setDuration(DURATION / 3)
.setListener(new AnimatorListenerAdapter() {
private boolean mFired;
@Override
public void onAnimationEnd(Animator animation) {
if (!mFired) {
mFired = true;
if (midBounce != null) {
midBounce.run();
}
v.animate().scaleX(1).scaleY(1).setListener(null).start();
}
}
}).start();
}
private final class UntilPager extends RelativeLayout {
private final ImageView mPrev;
private final ImageView mNext;
private final TextView mText1;
private final TextView mText2;
private TextView mText;
public UntilPager(Context context, Paint pathPaint, int iconSize) {
super(context);
mText1 = new TextView(mContext);
mText1.setTypeface(CONDENSED);
mText1.setTextColor(GRAY);
mText1.setGravity(Gravity.CENTER);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, iconSize);
addView(mText1, lp);
mText = mText1;
mText2 = new TextView(mContext);
mText2.setTypeface(CONDENSED);
mText2.setTextColor(GRAY);
mText2.setAlpha(0);
mText2.setGravity(Gravity.CENTER);
addView(mText2, lp);
lp = new LayoutParams(iconSize, iconSize);
final View v = new View(mContext);
v.setBackgroundColor(BACKGROUND);
addView(v, lp);
mPrev = new ImageView(mContext);
mPrev.setId(android.R.id.button1);
mPrev.setImageDrawable(sd(prevPath(iconSize), iconSize, pathPaint));
addView(mPrev, lp);
mPrev.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onNav(v, -1);
}
});
lp = new LayoutParams(iconSize, iconSize);
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
final View v2 = new View(mContext);
v2.setBackgroundColor(BACKGROUND);
addView(v2, lp);
mNext = new ImageView(mContext);
mNext.setId(android.R.id.button2);
mNext.setImageDrawable(sd(nextPath(iconSize), iconSize, pathPaint));
addView(mNext, lp);
mNext.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
onNav(v, 1);
}
});
updateState();
}
private void onNav(View v, int d) {
bounce(v, null);
if (mAdapter == null) {
return;
}
if (mAdapter.getExitConditionCount() == 1) {
horBounce(d);
return;
}
final int w = getWidth();
final float s = Math.signum(d);
final TextView current = mText;
final TextView other = mText == mText1 ? mText2 : mText1;
final ExitCondition ec = mAdapter.getExitCondition(d);
setText(other, ec);
other.setTranslationX(-s * w);
other.animate().translationX(0).alpha(1).setDuration(DURATION).start();
current.animate().translationX(s * w).alpha(0).setDuration(DURATION).start();
mText = other;
mAdapter.select(ec);
}
private void horBounce(int d) {
final int w = getWidth();
mText.animate()
.setDuration(BOUNCE_DURATION)
.translationX(Math.signum(d) * w / 20)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mText.animate().translationX(0).setListener(null).start();
}
}).start();
}
private void setText(final TextView textView, final ExitCondition ec) {
SpannableStringBuilder ss = new SpannableStringBuilder(ec.line1 + "\n" + ec.line2);
ss.setSpan(new RelativeSizeSpan(1.5f), (ec.line1 + "\n").length(), ss.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
if (ec.action != null) {
ss.setSpan(new CustomLinkSpan() {
@Override
public void onClick() {
// TODO wire up links
Toast.makeText(mContext, ec.action, Toast.LENGTH_SHORT).show();
}
}, (ec.line1 + "\n").length(), ss.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setMovementMethod(LinkMovementMethod.getInstance());
} else {
textView.setMovementMethod(null);
}
textView.setText(ss);
}
private void updateState() {
if (mAdapter == null) {
return;
}
setText(mText, mAdapter.getExitCondition(0));
}
private Path prevPath(int size) {
final int hp = size / 3;
final int vp = size / 4;
final Path p = new Path();
p.moveTo(size - hp, vp);
p.lineTo(hp, size / 2);
p.lineTo(size - hp, size - vp);
return p;
}
private Path nextPath(int size) {
final int hp = size / 3;
final int vp = size / 4;
Path p = new Path();
p.moveTo(hp, vp);
p.lineTo(size - hp, size / 2);
p.lineTo(hp, size - vp);
return p;
}
}
private abstract static class CustomLinkSpan extends URLSpan {
abstract public void onClick();
public CustomLinkSpan() {
super("#");
}
@Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(false);
ds.bgColor = BACKGROUND;
}
@Override
public void onClick(View widget) {
onClick();
}
}
public interface Adapter {
public static final int MODE_OFF = 0;
public static final int MODE_LIMITED = 1;
public static final int MODE_FULL = 2;
int getMode();
void setMode(int mode);
void select(ExitCondition ec);
void cancel();
void setCallbacks(Callbacks callbacks);
ExitCondition getExitCondition(int d);
int getExitConditionCount();
public static class ExitCondition {
public String summary;
public String line1;
public String line2;
public String action;
}
public interface Callbacks {
void onChanged();
}
}
private final class ModeSpinner extends Spinner {
public ModeSpinner(final Context context) {
super(context);
setBackgroundResource(R.drawable.spinner_default_holo_dark_am_no_underline);
final ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(mContext, 0) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (DEBUG) log("getView %s parent=%s", position, parent);
return getDropDownView(position, convertView, parent);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
if (DEBUG) log("getDropDownView %s cv=%s parent=%s",
position, convertView, parent);
final TextView tv = convertView != null ? (TextView) convertView
: new TextView(context);
final int mode = getItem(position);
tv.setText(modeToString(mode));
if (convertView == null) {
if (DEBUG) log(" setting up view");
tv.setTextColor(GRAY);
tv.setTypeface(CONDENSED);
tv.setAllCaps(true);
tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv.getTextSize() * 1.5f);
final int p = (int) tv.getTextSize() / 2;
if (parent instanceof ListView) {
tv.setPadding(p, p, p, p);
} else {
tv.setGravity(Gravity.CENTER_HORIZONTAL);
tv.setPadding(p, 0, p, 0);
}
}
tv.setOnTouchListener(new OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event) {
if (DEBUG) log("onTouch %s %s", tv.getText(),
MotionEvent.actionToString(event.getAction()));
if (mAdapter != null) {
mAdapter.setMode(mode);
}
return false;
}
});
return tv;
}
};
adapter.add(Adapter.MODE_LIMITED);
adapter.add(Adapter.MODE_FULL);
setAdapter(adapter);
}
public void updateState() {
int mode = mAdapter != null ? mAdapter.getMode() : Adapter.MODE_LIMITED;
if (mode == Adapter.MODE_OFF) {
mode = Adapter.MODE_LIMITED;
}
if (DEBUG) log("setSelectedMode " + mode);
for (int i = 0; i < getAdapter().getCount(); i++) {
if (getAdapter().getItem(i).equals(mode)) {
if (DEBUG) log(" setting selection = " + i);
setSelection(i, true);
return;
}
}
}
private String modeToString(int mode) {
if (mode == Adapter.MODE_LIMITED) return "Limited interruptions";
if (mode == Adapter.MODE_FULL) return "Zero interruptions";
throw new UnsupportedOperationException("Unsupported mode: " + mode);
}
}
private final class AlarmWarning extends LinearLayout {
public AlarmWarning(Context context) {
super(context);
setOrientation(HORIZONTAL);
final TextView tv = new TextView(mContext);
tv.setTextColor(GRAY);
tv.setGravity(Gravity.TOP);
tv.setTypeface(CONDENSED);
tv.setText(FULL_TEXT);
addView(tv);
final ImageView icon = new ImageView(mContext);
icon.setAlpha(.75f);
int size = (int)tv.getTextSize();
icon.setImageResource(android.R.drawable.ic_dialog_alert);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(size, size);
final int p = size / 4;
lp.bottomMargin = lp.topMargin = lp.rightMargin = lp.leftMargin = p;
addView(icon, 0, lp);
setPadding(p, 0, p, p);
}
public void updateState(boolean animate) {
final boolean visible = mAdapter != null && mAdapter.getMode() == Adapter.MODE_FULL;
final float alpha = visible ? 1 : 0;
if (alpha == getAlpha()) {
return;
}
if (animate) {
final boolean in = alpha == 1;
animate().alpha(alpha).setUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (mPeekable || mClosing) {
return;
}
float f = animation.getAnimatedFraction();
if (!in) {
f = 1 - f;
}
ZenModeView.this.mBottom = mUntilPager.getBottom()
+ (int)(mAlarmWarning.getMeasuredHeight() * f);
if (DEBUG) log("mBottom (AW) = " + mBottom);
requestLayout();
}
}).start();
} else {
setAlpha(alpha);
requestLayout();
}
}
}
private static class InfoWindow extends PopupWindow implements Runnable {
private final TextView mText;
public InfoWindow(Context context, String text) {
mText = new TextView(context);
mText.setTypeface(CONDENSED);
mText.setBackgroundColor(0xbb000000);
mText.setTextColor(0xffffffff);
mText.setText(text);
mText.setGravity(Gravity.CENTER);
setAnimationStyle(android.R.style.Animation_Toast);
setContentView(mText);
}
@Override
public void run() {
dismiss();
}
public void show(View over) {
setWidth(over.getMeasuredWidth());
setHeight(over.getMeasuredHeight());
final int[] loc = new int[2];
over.getLocationInWindow(loc);
showAtLocation(over, Gravity.NO_GRAVITY, loc[0], loc[1]);
over.postDelayed(this, INFO_WINDOW_DELAY);
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* Copyright (C) 2014 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.phone;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.AsyncTask;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import java.util.Arrays;
import java.util.List;
public class ZenModeViewAdapter implements ZenModeView.Adapter {
private static final String TAG = "ZenModeViewAdapter";
private final Context mContext;
private final ContentResolver mResolver;
private final Handler mHandler = new Handler();
private final List<ExitCondition> mExits = Arrays.asList(
newExit("Until you delete this", "Until", "You delete this"));
private Callbacks mCallbacks;
private int mExitIndex;
public ZenModeViewAdapter(Context context) {
mContext = context;
mResolver = mContext.getContentResolver();
mResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
false, new SettingsObserver(mHandler));
}
@Override
public int getMode() {
final int v = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
if (v == Settings.Global.ZEN_MODE_LIMITED) return MODE_LIMITED;
if (v == Settings.Global.ZEN_MODE_FULL) return MODE_FULL;
return MODE_OFF;
}
@Override
public void setMode(int mode) {
final int v = mode == MODE_LIMITED ? Settings.Global.ZEN_MODE_LIMITED
: mode == MODE_FULL ? Settings.Global.ZEN_MODE_FULL
: Settings.Global.ZEN_MODE_OFF;
AsyncTask.execute(new Runnable() {
@Override
public void run() {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ZEN_MODE, v);
}
});
}
@Override
public void cancel() {
if (mExitIndex != 0) {
mExitIndex = 0;
mHandler.post(mChange);
}
setMode(MODE_OFF);
}
@Override
public void setCallbacks(final Callbacks callbacks) {
mHandler.post(new Runnable() {
@Override
public void run() {
mCallbacks = callbacks;
}
});
}
@Override
public ExitCondition getExitCondition(int d) {
final int n = mExits.size();
final int i = (n + (mExitIndex + (int)Math.signum(d))) % n;
return mExits.get(i);
}
@Override
public int getExitConditionCount() {
return mExits.size();
}
@Override
public void select(ExitCondition ec) {
final int i = mExits.indexOf(ec);
if (i == -1 || i == mExitIndex) {
return;
}
mExitIndex = i;
mHandler.post(mChange);
}
private static ExitCondition newExit(String summary, String line1, String line2) {
final ExitCondition rt = new ExitCondition();
rt.summary = summary;
rt.line1 = line1;
rt.line2 = line2;
return rt;
}
private final Runnable mChange = new Runnable() {
public void run() {
if (mCallbacks == null) {
return;
}
try {
mCallbacks.onChanged();
} catch (Throwable t) {
Log.w(TAG, "Error dispatching onChanged to " + mCallbacks, t);
}
}
};
private final class SettingsObserver extends ContentObserver {
public SettingsObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
mChange.run(); // already on handler
}
}
}

View File

@@ -16,6 +16,8 @@
package com.android.server.notification;
import android.os.IBinder;
public interface NotificationDelegate {
void onSetDisabled(int status);
void onClearAll();
@@ -24,4 +26,5 @@ public interface NotificationDelegate {
void onNotificationError(String pkg, String tag, int id,
int uid, int initialPid, String message);
void onPanelRevealed();
boolean allowDisable(int what, IBinder token, String pkg);
}

View File

@@ -154,7 +154,7 @@ public class NotificationManagerService extends SystemService {
private long[] mFallbackVibrationPattern;
boolean mSystemReady;
int mDisabledNotifications;
private boolean mDisableNotificationAlerts;
NotificationRecord mSoundNotification;
NotificationRecord mVibrateNotification;
@@ -202,6 +202,19 @@ public class NotificationManagerService extends SystemService {
final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
private int mZenMode;
private int mPreZenAlarmVolume = -1;
private int mPreZenRingerMode = -1;
// temporary, until we update apps to provide metadata
private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.dialer",
"com.android.phone"
));
private static final Set<String> ALARM_PACKAGES = new HashSet<String>(Arrays.asList(
"com.google.android.deskclock"
));
private static final String EXTRA_INTERCEPT = "android.intercept";
private class NotificationListenerInfo implements IBinder.DeathRecipient {
INotificationListener listener;
ComponentName component;
@@ -869,8 +882,8 @@ public class NotificationManagerService extends SystemService {
@Override
public void onSetDisabled(int status) {
synchronized (mNotificationList) {
mDisabledNotifications = status;
if ((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0) {
mDisableNotificationAlerts = (status & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) != 0;
if (mDisableNotificationAlerts) {
// cancel whatever's going on
long identity = Binder.clearCallingIdentity();
try {
@@ -968,6 +981,14 @@ public class NotificationManagerService extends SystemService {
}
Binder.restoreCallingIdentity(ident);
}
@Override
public boolean allowDisable(int what, IBinder token, String pkg) {
if (mZenMode == Settings.Global.ZEN_MODE_FULL && isCall(pkg, null)) {
return false;
}
return true;
}
};
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@@ -1077,6 +1098,12 @@ public class NotificationManagerService extends SystemService {
private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
= Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
private final Uri ZEN_MODE
= Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
private final Uri MODE_RINGER
= Settings.Global.getUriFor(Settings.Global.MODE_RINGER);
SettingsObserver(Handler handler) {
super(handler);
}
@@ -1087,6 +1114,10 @@ public class NotificationManagerService extends SystemService {
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI,
false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(ZEN_MODE,
false, this);
resolver.registerContentObserver(MODE_RINGER,
false, this);
update(null);
}
@@ -1107,6 +1138,12 @@ public class NotificationManagerService extends SystemService {
if (uri == null || ENABLED_NOTIFICATION_LISTENERS_URI.equals(uri)) {
rebindListenerServices();
}
if (ZEN_MODE.equals(uri)) {
updateZenMode();
}
if (MODE_RINGER.equals(uri)) {
updateRingerMode();
}
}
}
@@ -1170,8 +1207,10 @@ public class NotificationManagerService extends SystemService {
// flag at least once and we'll go back to 0 after that.
if (0 == Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.DEVICE_PROVISIONED, 0)) {
mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS;
mDisableNotificationAlerts = true;
}
updateRingerMode();
updateZenMode();
// register for various Intents
IntentFilter filter = new IntentFilter();
@@ -1622,8 +1661,10 @@ public class NotificationManagerService extends SystemService {
pw.println(" mSoundNotification=" + mSoundNotification);
pw.println(" mVibrateNotification=" + mVibrateNotification);
pw.println(" mDisabledNotifications=0x"
+ Integer.toHexString(mDisabledNotifications));
pw.println(" mDisableNotificationAlerts=" + mDisableNotificationAlerts);
pw.println(" mZenMode=" + Settings.Global.zenModeToString(mZenMode));
pw.println(" mPreZenAlarmVolume=" + mPreZenAlarmVolume);
pw.println(" mPreZenRingerMode=" + mPreZenRingerMode);
pw.println(" mSystemReady=" + mSystemReady);
pw.println(" mArchive=" + mArchive.toString());
Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
@@ -1767,9 +1808,13 @@ public class NotificationManagerService extends SystemService {
return;
}
// Should this notification make noise, vibe, or use the LED?
final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD);
// Is this notification intercepted by zen mode?
final boolean intercept = shouldIntercept(pkg, notification);
notification.extras.putBoolean(EXTRA_INTERCEPT, intercept);
// Should this notification make noise, vibe, or use the LED?
final boolean canInterrupt = (score >= SCORE_INTERRUPTION_THRESHOLD) && !intercept;
if (DBG) Slog.v(TAG, "canInterrupt=" + canInterrupt + " intercept=" + intercept);
synchronized (mNotificationList) {
final StatusBarNotification n = new StatusBarNotification(
pkg, id, tag, callingUid, callingPid, score, notification, user);
@@ -1851,8 +1896,7 @@ public class NotificationManagerService extends SystemService {
}
// If we're not supposed to beep, vibrate, etc. then don't.
if (((mDisabledNotifications
& StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0)
if (!mDisableNotificationAlerts
&& (!(old != null
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
&& (r.getUserId() == UserHandle.USER_ALL ||
@@ -1860,7 +1904,7 @@ public class NotificationManagerService extends SystemService {
&& canInterrupt
&& mSystemReady
&& mAudioManager != null) {
if (DBG) Slog.v(TAG, "Interrupting!");
// sound
// should we use the default notification sound? (indicated either by
@@ -1905,6 +1949,8 @@ public class NotificationManagerService extends SystemService {
final IRingtonePlayer player =
mAudioManager.getRingtonePlayer();
if (player != null) {
if (DBG) Slog.v(TAG, "Playing sound " + soundUri
+ " on stream " + audioStreamType);
player.playAsync(soundUri, user, looping, audioStreamType);
}
} catch (RemoteException e) {
@@ -2437,4 +2483,67 @@ public class NotificationManagerService extends SystemService {
updateLightsLocked();
}
}
private void updateRingerMode() {
final int ringerMode = Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.MODE_RINGER, -1);
final boolean nonSilentRingerMode = ringerMode == AudioManager.RINGER_MODE_NORMAL
|| ringerMode == AudioManager.RINGER_MODE_VIBRATE;
if (mZenMode == Settings.Global.ZEN_MODE_FULL && nonSilentRingerMode) {
Settings.Global.putInt(getContext().getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
}
}
private void updateZenMode() {
mZenMode = Settings.Global.getInt(getContext().getContentResolver(),
Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
if (DBG) Slog.d(TAG, "updateZenMode " + Settings.Global.zenModeToString(mZenMode));
if (mAudioManager != null) {
if (mZenMode == Settings.Global.ZEN_MODE_FULL) {
// calls vibrate if ringer mode = vibrate, so set the ringer mode as well
mPreZenRingerMode = mAudioManager.getRingerMode();
if (DBG) Slog.d(TAG, "Muting calls mPreZenRingerMode=" + mPreZenRingerMode);
mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
// alarms don't simply respect mute, so set the volume as well
mPreZenAlarmVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
if (DBG) Slog.d(TAG, "Muting alarms mPreZenAlarmVolume=" + mPreZenAlarmVolume);
mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, true);
mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 0, 0);
} else {
if (DBG) Slog.d(TAG, "Unmuting calls");
mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
if (mPreZenRingerMode != -1) {
if (DBG) Slog.d(TAG, "Restoring ringer mode to " + mPreZenRingerMode);
mAudioManager.setRingerMode(mPreZenRingerMode);
mPreZenRingerMode = -1;
}
if (DBG) Slog.d(TAG, "Unmuting alarms");
mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, false);
if (mPreZenAlarmVolume != -1) {
if (DBG) Slog.d(TAG, "Restoring STREAM_ALARM to " + mPreZenAlarmVolume);
mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mPreZenAlarmVolume, 0);
mPreZenAlarmVolume = -1;
}
}
}
}
private boolean isCall(String pkg, Notification n) {
return CALL_PACKAGES.contains(pkg);
}
private boolean isAlarm(String pkg, Notification n) {
return ALARM_PACKAGES.contains(pkg);
}
private boolean shouldIntercept(String pkg, Notification n) {
if (mZenMode == Settings.Global.ZEN_MODE_LIMITED) {
return !isCall(pkg, n) && !isAlarm(pkg, n);
} else if (mZenMode == Settings.Global.ZEN_MODE_FULL) {
return true;
}
return false;
}
}

View File

@@ -23,9 +23,7 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.util.Slog;
@@ -207,6 +205,10 @@ public class StatusBarManagerService extends IStatusBarService.Stub
@Override
public void disable(int what, IBinder token, String pkg) {
if (!mNotificationDelegate.allowDisable(what, token, pkg)) {
if (SPEW) Slog.d(TAG, "Blocking disable request from " + pkg);
return;
}
disableInternal(mCurrentUserId, what, token, pkg);
}
@@ -676,26 +678,4 @@ public class StatusBarManagerService extends IStatusBarService.Stub
}
}
}
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)) {
collapsePanels();
}
/*
else if (Telephony.Intents.SPN_STRINGS_UPDATED_ACTION.equals(action)) {
updateNetworkName(intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_SPN, false),
intent.getStringExtra(Telephony.Intents.EXTRA_SPN),
intent.getBooleanExtra(Telephony.Intents.EXTRA_SHOW_PLMN, false),
intent.getStringExtra(Telephony.Intents.EXTRA_PLMN));
}
else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
updateResources();
}
*/
}
};
}