Merge "Introduce zen mode."
@@ -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.
|
||||
|
||||
|
After Width: | Height: | Size: 408 B |
BIN
packages/SystemUI/res/drawable-hdpi/stat_sys_zen_full.png
Normal file
|
After Width: | Height: | Size: 745 B |
BIN
packages/SystemUI/res/drawable-hdpi/stat_sys_zen_limited.png
Normal file
|
After Width: | Height: | Size: 614 B |
|
After Width: | Height: | Size: 293 B |
BIN
packages/SystemUI/res/drawable-mdpi/stat_sys_zen_full.png
Normal file
|
After Width: | Height: | Size: 522 B |
BIN
packages/SystemUI/res/drawable-mdpi/stat_sys_zen_limited.png
Normal file
|
After Width: | Height: | Size: 444 B |
|
After Width: | Height: | Size: 404 B |
BIN
packages/SystemUI/res/drawable-xhdpi/stat_sys_zen_full.png
Normal file
|
After Width: | Height: | Size: 989 B |
BIN
packages/SystemUI/res/drawable-xhdpi/stat_sys_zen_limited.png
Normal file
|
After Width: | Height: | Size: 765 B |
|
After Width: | Height: | Size: 456 B |
BIN
packages/SystemUI/res/drawable-xxhdpi/stat_sys_zen_full.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
packages/SystemUI/res/drawable-xxhdpi/stat_sys_zen_limited.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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=");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
*/
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||