├── README.md ├── LICENSE ├── notification ├── RedactionSettingsStandalone.java ├── NotificationAccessSettings.java ├── ZenModeScheduleDaysSelection.java ├── ZenModeVoiceActivity.java ├── ZenModeSettingsBase.java ├── SettingPref.java ├── ZenModeExternalRuleSettings.java ├── ZenModeSettings.java ├── NotificationManagerSettings.java ├── NotificationBackend.java ├── ZenModeConditionSelection.java ├── RedactionInterstitial.java ├── ManagedServiceSettings.java ├── VolumeSeekBarPreference.java ├── ServiceListing.java ├── ZenAccessSettings.java ├── ZenModeEventRuleSettings.java ├── IncreasingRingVolumePreference.java ├── ZenModePrioritySettings.java ├── ZenRuleNameDialog.java ├── ZenModeRuleSettingsBase.java ├── ZenModeScheduleRuleSettings.java ├── NotificationStation.java └── ZenModeAutomationSettings.java ├── dumpvar.mk └── notificationlight ├── AlphaPatternDrawable.java ├── ColorPanelView.java ├── BatteryLightSettings.java └── ApplicationLightPreference.java /README.md: -------------------------------------------------------------------------------- 1 | # lineagex86 2 | linage os on android-x86 platform 3 | 4 | $ mkdir android-x86 5 | 6 | $ cd android-x86 7 | 8 | $ repo init -u git://git.osdn.net/gitroot/android-x86/manifest -b $branch 9 | 10 | replace cm.xml .repo/manifests 11 | 12 | $ repo init -m cm.xml 13 | 14 | $ repo sync --no-tags --no-clone-bundle 15 | 16 | $ cd packages/services/Telecomm/ 17 | 18 | $ git revert 1d0ca65e7b41d2728c226ae9284df6ff8e322db3 19 | 20 | $ git revert 01882f2df414e2946baf07bef93b3216105a74ec 21 | 22 | as of https://github.com/LineageOS/android_build/commit/a0300d400ace78f8045f17c24db33ce6d38dc072 23 | and previous rebase commits 24 | 25 | replace Makefile to build/core/ 26 | 27 | replace dumpvar.mk build/core/ 28 | 29 | replace envsetup.sh build/ 30 | 31 | replace folder notfication and notificationlight packages/apps/Settings/src/com/android/settings 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Sahaj Sarup 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /notification/RedactionSettingsStandalone.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import com.android.settings.R; 20 | 21 | import android.content.Intent; 22 | import com.android.settings.SettingsActivity; 23 | import com.android.settings.notification.RedactionInterstitial.RedactionInterstitialFragment; 24 | 25 | /** Wrapper to allow external activites to jump directly to the {@link RedactionInterstitial} */ 26 | public class RedactionSettingsStandalone extends SettingsActivity { 27 | 28 | @Override 29 | public Intent getIntent() { 30 | Intent modIntent = new Intent(super.getIntent()); 31 | modIntent.putExtra(EXTRA_SHOW_FRAGMENT, RedactionInterstitialFragment.class.getName()) 32 | .putExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, true) 33 | .putExtra(EXTRA_PREFS_SET_BACK_TEXT, (String) null) 34 | .putExtra(EXTRA_PREFS_SET_NEXT_TEXT, getString( 35 | R.string.app_notifications_dialog_done)); 36 | return modIntent; 37 | } 38 | 39 | @Override 40 | protected boolean isValidFragment(String fragmentName) { 41 | return RedactionInterstitialFragment.class.getName().equals(fragmentName); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /notification/NotificationAccessSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.content.Context; 20 | import android.content.pm.PackageManager; 21 | import android.provider.Settings; 22 | import android.service.notification.NotificationListenerService; 23 | 24 | import com.android.internal.logging.MetricsLogger; 25 | import com.android.settings.R; 26 | 27 | public class NotificationAccessSettings extends ManagedServiceSettings { 28 | private static final String TAG = NotificationAccessSettings.class.getSimpleName(); 29 | private static final Config CONFIG = getNotificationListenerConfig(); 30 | 31 | private static Config getNotificationListenerConfig() { 32 | final Config c = new Config(); 33 | c.tag = TAG; 34 | c.setting = Settings.Secure.ENABLED_NOTIFICATION_LISTENERS; 35 | c.intentAction = NotificationListenerService.SERVICE_INTERFACE; 36 | c.permission = android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE; 37 | c.noun = "notification listener"; 38 | c.warningDialogTitle = R.string.notification_listener_security_warning_title; 39 | c.warningDialogSummary = R.string.notification_listener_security_warning_summary; 40 | c.emptyText = R.string.no_notification_listeners; 41 | return c; 42 | } 43 | 44 | @Override 45 | protected int getMetricsCategory() { 46 | return MetricsLogger.NOTIFICATION_ACCESS; 47 | } 48 | 49 | @Override 50 | protected Config getConfig() { 51 | return CONFIG; 52 | } 53 | 54 | public static int getListenersCount(PackageManager pm) { 55 | return ServiceListing.getServicesCount(CONFIG, pm); 56 | } 57 | 58 | public static int getEnabledListenersCount(Context context) { 59 | return ServiceListing.getEnabledServicesCount(CONFIG, context); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /dumpvar.mk: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------- 2 | # the setpath shell function in envsetup.sh uses this to figure out 3 | # what to add to the path given the config we have chosen. 4 | ifeq ($(CALLED_FROM_SETUP),true) 5 | 6 | ifneq ($(filter /%,$(HOST_OUT_EXECUTABLES)),) 7 | ABP:=$(HOST_OUT_EXECUTABLES) 8 | else 9 | ABP:=$(PWD)/$(HOST_OUT_EXECUTABLES) 10 | endif 11 | 12 | ANDROID_BUILD_PATHS := $(ABP) 13 | ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG) 14 | ANDROID_GCC_PREBUILTS := prebuilts/gcc/$(HOST_PREBUILT_TAG) 15 | 16 | # The "dumpvar" stuff lets you say something like 17 | # 18 | # CALLED_FROM_SETUP=true \ 19 | # make -f config/envsetup.make dumpvar-TARGET_OUT 20 | # or 21 | # CALLED_FROM_SETUP=true \ 22 | # make -f config/envsetup.make dumpvar-abs-HOST_OUT_EXECUTABLES 23 | # 24 | # The plain (non-abs) version just dumps the value of the named variable. 25 | # The "abs" version will treat the variable as a path, and dumps an 26 | # absolute path to it. 27 | # 28 | dumpvar_goals := \ 29 | $(strip $(patsubst dumpvar-%,%,$(filter dumpvar-%,$(MAKECMDGOALS)))) 30 | ifdef dumpvar_goals 31 | 32 | ifneq ($(words $(dumpvar_goals)),1) 33 | $(error Only one "dumpvar-" goal allowed. Saw "$(MAKECMDGOALS)") 34 | endif 35 | 36 | # If the goal is of the form "dumpvar-abs-VARNAME", then 37 | # treat VARNAME as a path and return the absolute path to it. 38 | absolute_dumpvar := $(strip $(filter abs-%,$(dumpvar_goals))) 39 | ifdef absolute_dumpvar 40 | dumpvar_goals := $(patsubst abs-%,%,$(dumpvar_goals)) 41 | ifneq ($(filter /%,$($(dumpvar_goals))),) 42 | DUMPVAR_VALUE := $($(dumpvar_goals)) 43 | else 44 | DUMPVAR_VALUE := $(PWD)/$($(dumpvar_goals)) 45 | endif 46 | dumpvar_target := dumpvar-abs-$(dumpvar_goals) 47 | else 48 | DUMPVAR_VALUE := $($(dumpvar_goals)) 49 | dumpvar_target := dumpvar-$(dumpvar_goals) 50 | endif 51 | 52 | .PHONY: $(dumpvar_target) 53 | $(dumpvar_target): 54 | @echo $(DUMPVAR_VALUE) 55 | 56 | endif # dumpvar_goals 57 | 58 | ifneq ($(dumpvar_goals),report_config) 59 | PRINT_BUILD_CONFIG:= 60 | endif 61 | 62 | endif # CALLED_FROM_SETUP 63 | 64 | 65 | ifneq ($(PRINT_BUILD_CONFIG),) 66 | HOST_OS_EXTRA:=$(shell python -c "import platform; print(platform.platform())") 67 | $(info ============================================) 68 | $(info PLATFORM_VERSION_CODENAME=$(PLATFORM_VERSION_CODENAME)) 69 | $(info PLATFORM_VERSION=$(PLATFORM_VERSION)) 70 | $(info LINEAGE_VERSION=$(LINEAGE_VERSION)) 71 | $(info TARGET_PRODUCT=$(TARGET_PRODUCT)) 72 | $(info TARGET_BUILD_VARIANT=$(TARGET_BUILD_VARIANT)) 73 | $(info TARGET_BUILD_TYPE=$(TARGET_BUILD_TYPE)) 74 | $(info TARGET_BUILD_APPS=$(TARGET_BUILD_APPS)) 75 | $(info TARGET_ARCH=$(TARGET_ARCH)) 76 | $(info TARGET_ARCH_VARIANT=$(TARGET_ARCH_VARIANT)) 77 | $(info TARGET_CPU_VARIANT=$(TARGET_CPU_VARIANT)) 78 | $(info TARGET_2ND_ARCH=$(TARGET_2ND_ARCH)) 79 | $(info TARGET_2ND_ARCH_VARIANT=$(TARGET_2ND_ARCH_VARIANT)) 80 | $(info TARGET_2ND_CPU_VARIANT=$(TARGET_2ND_CPU_VARIANT)) 81 | $(info HOST_ARCH=$(HOST_ARCH)) 82 | $(info HOST_OS=$(HOST_OS)) 83 | $(info HOST_OS_EXTRA=$(HOST_OS_EXTRA)) 84 | $(info HOST_BUILD_TYPE=$(HOST_BUILD_TYPE)) 85 | $(info BUILD_ID=$(BUILD_ID)) 86 | $(info OUT_DIR=$(OUT_DIR)) 87 | ifeq ($(CYNGN_TARGET),true) 88 | $(info CYNGN_TARGET=$(CYNGN_TARGET)) 89 | $(info CYNGN_FEATURES=$(CYNGN_FEATURES)) 90 | endif 91 | $(info ============================================) 92 | endif 93 | -------------------------------------------------------------------------------- /notification/ZenModeScheduleDaysSelection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.content.Context; 20 | import android.util.SparseBooleanArray; 21 | import android.view.LayoutInflater; 22 | import android.widget.CheckBox; 23 | import android.widget.CompoundButton; 24 | import android.widget.CompoundButton.OnCheckedChangeListener; 25 | import android.widget.LinearLayout; 26 | import android.widget.ScrollView; 27 | 28 | import com.android.settings.R; 29 | 30 | import java.text.SimpleDateFormat; 31 | import java.util.Arrays; 32 | import java.util.Calendar; 33 | 34 | public class ZenModeScheduleDaysSelection extends ScrollView { 35 | public static final int[] DAYS = { 36 | Calendar.SUNDAY, 37 | Calendar.MONDAY, 38 | Calendar.TUESDAY, 39 | Calendar.WEDNESDAY, 40 | Calendar.THURSDAY, 41 | Calendar.FRIDAY, 42 | Calendar.SATURDAY, 43 | }; 44 | 45 | // per-instance to ensure we're always using the current locale 46 | private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEEE"); 47 | private final SparseBooleanArray mDays = new SparseBooleanArray(); 48 | private final LinearLayout mLayout; 49 | 50 | public ZenModeScheduleDaysSelection(Context context, int[] days) { 51 | super(context); 52 | mLayout = new LinearLayout(mContext); 53 | final int hPad = context.getResources() 54 | .getDimensionPixelSize(R.dimen.zen_schedule_day_margin); 55 | mLayout.setPadding(hPad, 0, hPad, 0); 56 | addView(mLayout); 57 | if (days != null) { 58 | for (int i = 0; i < days.length; i++) { 59 | mDays.put(days[i], true); 60 | } 61 | } 62 | mLayout.setOrientation(LinearLayout.VERTICAL); 63 | final Calendar c = Calendar.getInstance(); 64 | int i = c.getFirstDayOfWeek() - 1; 65 | final LayoutInflater inflater = LayoutInflater.from(context); 66 | for (int d = 0; d < DAYS.length; d++) { 67 | final int day = DAYS[i]; 68 | final CheckBox checkBox = (CheckBox) inflater.inflate(R.layout.zen_schedule_rule_day, 69 | this, false); 70 | c.set(Calendar.DAY_OF_WEEK, day); 71 | checkBox.setText(mDayFormat.format(c.getTime())); 72 | checkBox.setChecked(mDays.get(day)); 73 | checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { 74 | @Override 75 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 76 | mDays.put(day, isChecked); 77 | onChanged(getDays()); 78 | } 79 | }); 80 | mLayout.addView(checkBox); 81 | i = ++i % DAYS.length; 82 | } 83 | } 84 | 85 | private int[] getDays() { 86 | final SparseBooleanArray rt = new SparseBooleanArray(mDays.size()); 87 | for (int i = 0; i < mDays.size(); i++) { 88 | final int day = mDays.keyAt(i); 89 | if (!mDays.valueAt(i)) continue; 90 | rt.put(day, true); 91 | } 92 | final int[] rta = new int[rt.size()]; 93 | for (int i = 0; i < rta.length; i++) { 94 | rta[i] = rt.keyAt(i); 95 | } 96 | Arrays.sort(rta); 97 | return rta; 98 | } 99 | 100 | protected void onChanged(int[] days) { 101 | // event hook for subclasses 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /notificationlight/AlphaPatternDrawable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Daniel Nilsson 3 | * Copyright (C) 2012 The CyanogenMod Project 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.android.settings.notificationlight; 19 | 20 | import android.graphics.Bitmap; 21 | import android.graphics.Canvas; 22 | import android.graphics.ColorFilter; 23 | import android.graphics.Paint; 24 | import android.graphics.Rect; 25 | import android.graphics.Bitmap.Config; 26 | import android.graphics.drawable.Drawable; 27 | 28 | /** 29 | * This drawable that draws a simple white and gray chess board pattern. It's 30 | * pattern you will often see as a background behind a partly transparent image 31 | * in many applications. 32 | * 33 | * @author Daniel Nilsson 34 | */ 35 | public class AlphaPatternDrawable extends Drawable { 36 | 37 | private int mRectangleSize = 10; 38 | 39 | private Paint mPaint = new Paint(); 40 | private Paint mPaintWhite = new Paint(); 41 | private Paint mPaintGray = new Paint(); 42 | 43 | private int numRectanglesHorizontal; 44 | private int numRectanglesVertical; 45 | 46 | /** 47 | * Bitmap in which the pattern will be cached. 48 | */ 49 | private Bitmap mBitmap; 50 | 51 | public AlphaPatternDrawable(int rectangleSize) { 52 | mRectangleSize = rectangleSize; 53 | mPaintWhite.setColor(0xffffffff); 54 | mPaintGray.setColor(0xffcbcbcb); 55 | } 56 | 57 | @Override 58 | public void draw(Canvas canvas) { 59 | if (mBitmap != null) { 60 | canvas.drawBitmap(mBitmap, null, getBounds(), mPaint); 61 | } 62 | } 63 | 64 | @Override 65 | public int getOpacity() { 66 | return 0; 67 | } 68 | 69 | @Override 70 | public void setAlpha(int alpha) { 71 | throw new UnsupportedOperationException("Alpha is not supported by this drawwable."); 72 | } 73 | 74 | @Override 75 | public void setColorFilter(ColorFilter cf) { 76 | throw new UnsupportedOperationException("ColorFilter is not supported by this drawwable."); 77 | } 78 | 79 | @Override 80 | protected void onBoundsChange(Rect bounds) { 81 | super.onBoundsChange(bounds); 82 | 83 | int height = bounds.height(); 84 | int width = bounds.width(); 85 | 86 | numRectanglesHorizontal = (int) Math.ceil((width / mRectangleSize)); 87 | numRectanglesVertical = (int) Math.ceil(height / mRectangleSize); 88 | 89 | generatePatternBitmap(); 90 | } 91 | 92 | /** 93 | * This will generate a bitmap with the pattern as big as the rectangle we 94 | * were allow to draw on. We do this to cache the bitmap so we don't need 95 | * to recreate it each time draw() is called since it takes a few 96 | * milliseconds. 97 | */ 98 | private void generatePatternBitmap() { 99 | 100 | if (getBounds().width() <= 0 || getBounds().height() <= 0) { 101 | return; 102 | } 103 | 104 | mBitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Config.ARGB_8888); 105 | Canvas canvas = new Canvas(mBitmap); 106 | 107 | Rect r = new Rect(); 108 | boolean verticalStartWhite = true; 109 | for (int i = 0; i <= numRectanglesVertical; i++) { 110 | boolean isWhite = verticalStartWhite; 111 | for (int j = 0; j <= numRectanglesHorizontal; j++) { 112 | r.top = i * mRectangleSize; 113 | r.left = j * mRectangleSize; 114 | r.bottom = r.top + mRectangleSize; 115 | r.right = r.left + mRectangleSize; 116 | 117 | canvas.drawRect(r, isWhite ? mPaintWhite : mPaintGray); 118 | 119 | isWhite = !isWhite; 120 | } 121 | 122 | verticalStartWhite = !verticalStartWhite; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /notificationlight/ColorPanelView.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 Daniel Nilsson 3 | * Copyright (C) 2012 The CyanogenMod Project 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.android.settings.notificationlight; 19 | 20 | import android.content.Context; 21 | import android.graphics.Canvas; 22 | import android.graphics.Paint; 23 | import android.graphics.RectF; 24 | import android.util.AttributeSet; 25 | import android.view.View; 26 | 27 | /** 28 | * This class draws a panel which which will be filled with a color which can be 29 | * set. It can be used to show the currently selected color which you will get 30 | * from the {@link ColorPickerView}. 31 | * 32 | * @author Daniel Nilsson 33 | */ 34 | public class ColorPanelView extends View { 35 | 36 | /** 37 | * The width in pixels of the border surrounding the color panel. 38 | */ 39 | private final static float BORDER_WIDTH_PX = 1; 40 | 41 | private static float mDensity = 1f; 42 | 43 | private int mBorderColor = 0xff6E6E6E; 44 | private int mColor = 0xff000000; 45 | 46 | private Paint mBorderPaint; 47 | private Paint mColorPaint; 48 | 49 | private RectF mDrawingRect; 50 | private RectF mColorRect; 51 | 52 | private AlphaPatternDrawable mAlphaPattern; 53 | 54 | public ColorPanelView(Context context) { 55 | this(context, null); 56 | } 57 | 58 | public ColorPanelView(Context context, AttributeSet attrs) { 59 | this(context, attrs, 0); 60 | } 61 | 62 | public ColorPanelView(Context context, AttributeSet attrs, int defStyle) { 63 | super(context, attrs, defStyle); 64 | 65 | init(); 66 | } 67 | 68 | private void init() { 69 | mBorderPaint = new Paint(); 70 | mColorPaint = new Paint(); 71 | mDensity = getContext().getResources().getDisplayMetrics().density; 72 | } 73 | 74 | @Override 75 | protected void onDraw(Canvas canvas) { 76 | 77 | final RectF rect = mColorRect; 78 | 79 | if (BORDER_WIDTH_PX > 0) { 80 | mBorderPaint.setColor(mBorderColor); 81 | canvas.drawRect(mDrawingRect, mBorderPaint); 82 | } 83 | 84 | if (mAlphaPattern != null) { 85 | mAlphaPattern.draw(canvas); 86 | } 87 | 88 | mColorPaint.setColor(mColor); 89 | 90 | canvas.drawRect(rect, mColorPaint); 91 | } 92 | 93 | @Override 94 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 95 | 96 | int width = MeasureSpec.getSize(widthMeasureSpec); 97 | int height = MeasureSpec.getSize(heightMeasureSpec); 98 | 99 | setMeasuredDimension(width, height); 100 | } 101 | 102 | @Override 103 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 104 | super.onSizeChanged(w, h, oldw, oldh); 105 | 106 | mDrawingRect = new RectF(); 107 | mDrawingRect.left = getPaddingLeft(); 108 | mDrawingRect.right = w - getPaddingRight(); 109 | mDrawingRect.top = getPaddingTop(); 110 | mDrawingRect.bottom = h - getPaddingBottom(); 111 | 112 | setUpColorRect(); 113 | 114 | } 115 | 116 | private void setUpColorRect() { 117 | final RectF dRect = mDrawingRect; 118 | 119 | float left = dRect.left + BORDER_WIDTH_PX; 120 | float top = dRect.top + BORDER_WIDTH_PX; 121 | float bottom = dRect.bottom - BORDER_WIDTH_PX; 122 | float right = dRect.right - BORDER_WIDTH_PX; 123 | 124 | mColorRect = new RectF(left, top, right, bottom); 125 | 126 | mAlphaPattern = new AlphaPatternDrawable((int) (5 * mDensity)); 127 | 128 | mAlphaPattern.setBounds(Math.round(mColorRect.left), 129 | Math.round(mColorRect.top), 130 | Math.round(mColorRect.right), 131 | Math.round(mColorRect.bottom)); 132 | 133 | } 134 | 135 | /** 136 | * Set the color that should be shown by this view. 137 | * 138 | * @param color 139 | */ 140 | public void setColor(int color) { 141 | mColor = color; 142 | invalidate(); 143 | } 144 | 145 | /** 146 | * Get the color currently show by this view. 147 | * 148 | * @return 149 | */ 150 | public int getColor() { 151 | return mColor; 152 | } 153 | 154 | /** 155 | * Set the color of the border surrounding the panel. 156 | * 157 | * @param color 158 | */ 159 | public void setBorderColor(int color) { 160 | mBorderColor = color; 161 | invalidate(); 162 | } 163 | 164 | /** 165 | * Get the color of the border surrounding the panel. 166 | */ 167 | public int getBorderColor() { 168 | return mBorderColor; 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /notification/ZenModeVoiceActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES; 20 | import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED; 21 | 22 | import com.android.settings.R; 23 | import com.android.settings.utils.VoiceSettingsActivity; 24 | 25 | import android.app.NotificationManager; 26 | import android.content.Context; 27 | import android.content.Intent; 28 | import android.content.res.Resources; 29 | import android.os.UserHandle; 30 | import android.media.AudioManager; 31 | import android.provider.Settings.Global; 32 | import android.service.notification.Condition; 33 | import android.service.notification.ZenModeConfig; 34 | import android.text.format.DateFormat; 35 | import android.util.Log; 36 | 37 | import java.util.Locale; 38 | 39 | /** 40 | * Activity for modifying the Zen mode (Do not disturb) by voice 41 | * using the Voice Interaction API. 42 | */ 43 | public class ZenModeVoiceActivity extends VoiceSettingsActivity { 44 | private static final String TAG = "ZenModeVoiceActivity"; 45 | private static final int MINUTES_MS = 60 * 1000; 46 | 47 | @Override 48 | protected boolean onVoiceSettingInteraction(Intent intent) { 49 | if (intent.hasExtra(EXTRA_DO_NOT_DISTURB_MODE_ENABLED)) { 50 | int minutes = intent.getIntExtra(EXTRA_DO_NOT_DISTURB_MODE_MINUTES, -1); 51 | Condition condition = null; 52 | int mode = Global.ZEN_MODE_OFF; 53 | 54 | if (intent.getBooleanExtra(EXTRA_DO_NOT_DISTURB_MODE_ENABLED, false)) { 55 | if (minutes > 0) { 56 | condition = ZenModeConfig.toTimeCondition(this, minutes, UserHandle.myUserId()); 57 | } 58 | mode = Global.ZEN_MODE_ALARMS; 59 | } 60 | setZenModeConfig(mode, condition); 61 | 62 | AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); 63 | if (audioManager != null) { 64 | // Show the current Zen Mode setting. 65 | audioManager.adjustStreamVolume(AudioManager.STREAM_NOTIFICATION, 66 | AudioManager.ADJUST_SAME, 67 | AudioManager.FLAG_SHOW_UI); 68 | } 69 | notifySuccess(getChangeSummary(mode, minutes)); 70 | } else { 71 | Log.v(TAG, "Missing extra android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED"); 72 | finish(); 73 | } 74 | return false; 75 | } 76 | 77 | private void setZenModeConfig(int mode, Condition condition) { 78 | if (condition != null) { 79 | NotificationManager.from(this).setZenMode(mode, condition.id, TAG); 80 | } else { 81 | NotificationManager.from(this).setZenMode(mode, null, TAG); 82 | } 83 | } 84 | 85 | /** 86 | * Produce a summary of the Zen mode change to be read aloud as TTS. 87 | */ 88 | private CharSequence getChangeSummary(int mode, int minutes) { 89 | int indefinite = -1; 90 | int byMinute = -1; 91 | int byHour = -1; 92 | int byTime = -1; 93 | 94 | switch (mode) { 95 | case Global.ZEN_MODE_ALARMS: 96 | indefinite = R.string.zen_mode_summary_alarms_only_indefinite; 97 | byMinute = R.plurals.zen_mode_summary_alarms_only_by_minute; 98 | byHour = R.plurals.zen_mode_summary_alarms_only_by_hour; 99 | byTime = R.string.zen_mode_summary_alarms_only_by_time; 100 | break; 101 | case Global.ZEN_MODE_OFF: 102 | indefinite = R.string.zen_mode_summary_always; 103 | break; 104 | }; 105 | 106 | if (minutes < 0 || mode == Global.ZEN_MODE_OFF) { 107 | return getString(indefinite); 108 | } 109 | 110 | long time = System.currentTimeMillis() + minutes * MINUTES_MS; 111 | String skeleton = DateFormat.is24HourFormat(this, UserHandle.myUserId()) ? "Hm" : "hma"; 112 | String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); 113 | CharSequence formattedTime = DateFormat.format(pattern, time); 114 | Resources res = getResources(); 115 | 116 | if (minutes < 60) { 117 | return res.getQuantityString(byMinute, minutes, minutes, formattedTime); 118 | } else if (minutes % 60 != 0) { 119 | return res.getString(byTime, formattedTime); 120 | } else { 121 | int hours = minutes / 60; 122 | return res.getQuantityString(byHour, hours, hours, formattedTime); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /notification/ZenModeSettingsBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.app.NotificationManager; 20 | import android.content.Context; 21 | import android.database.ContentObserver; 22 | import android.net.Uri; 23 | import android.os.Bundle; 24 | import android.os.Handler; 25 | import android.os.UserManager; 26 | import android.provider.Settings; 27 | import android.provider.Settings.Global; 28 | import android.service.notification.ZenModeConfig; 29 | import android.util.Log; 30 | 31 | import com.android.settings.RestrictedSettingsFragment; 32 | 33 | import java.util.Objects; 34 | 35 | abstract public class ZenModeSettingsBase extends RestrictedSettingsFragment { 36 | protected static final String TAG = "ZenModeSettings"; 37 | protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 38 | 39 | private final Handler mHandler = new Handler(); 40 | private final SettingsObserver mSettingsObserver = new SettingsObserver(); 41 | 42 | protected Context mContext; 43 | protected ZenModeConfig mConfig; 44 | protected int mZenMode; 45 | 46 | abstract protected void onZenModeChanged(); 47 | abstract protected void onZenModeConfigChanged(); 48 | 49 | public ZenModeSettingsBase() { 50 | super(UserManager.DISALLOW_ADJUST_VOLUME); 51 | } 52 | 53 | @Override 54 | public void onCreate(Bundle icicle) { 55 | super.onCreate(icicle); 56 | mContext = getActivity(); 57 | updateZenMode(false /*fireChanged*/); 58 | updateZenModeConfig(false /*fireChanged*/); 59 | if (DEBUG) Log.d(TAG, "Loaded mConfig=" + mConfig); 60 | } 61 | 62 | @Override 63 | public void onResume() { 64 | super.onResume(); 65 | updateZenMode(true /*fireChanged*/); 66 | updateZenModeConfig(true /*fireChanged*/); 67 | mSettingsObserver.register(); 68 | if (isUiRestricted()) { 69 | finish(); 70 | } 71 | } 72 | 73 | @Override 74 | public void onPause() { 75 | super.onPause(); 76 | mSettingsObserver.unregister(); 77 | } 78 | 79 | private void updateZenMode(boolean fireChanged) { 80 | final int zenMode = Settings.Global.getInt(getContentResolver(), Global.ZEN_MODE, mZenMode); 81 | if (zenMode == mZenMode) return; 82 | mZenMode = zenMode; 83 | if (DEBUG) Log.d(TAG, "updateZenMode mZenMode=" + mZenMode); 84 | if (fireChanged) { 85 | onZenModeChanged(); 86 | } 87 | } 88 | 89 | private void updateZenModeConfig(boolean fireChanged) { 90 | final ZenModeConfig config = getZenModeConfig(); 91 | if (Objects.equals(config, mConfig)) return; 92 | mConfig = config; 93 | if (DEBUG) Log.d(TAG, "updateZenModeConfig mConfig=" + mConfig); 94 | if (fireChanged) { 95 | onZenModeConfigChanged(); 96 | } 97 | } 98 | 99 | protected boolean setZenModeConfig(ZenModeConfig config) { 100 | final String reason = getClass().getSimpleName(); 101 | final boolean success = NotificationManager.from(mContext).setZenModeConfig(config, reason); 102 | if (success) { 103 | mConfig = config; 104 | if (DEBUG) Log.d(TAG, "Saved mConfig=" + mConfig); 105 | onZenModeConfigChanged(); 106 | } 107 | return success; 108 | } 109 | 110 | protected void setZenMode(int zenMode, Uri conditionId) { 111 | NotificationManager.from(mContext).setZenMode(zenMode, conditionId, TAG); 112 | } 113 | 114 | protected static boolean isScheduleSupported(Context context) { 115 | return NotificationManager.from(context) 116 | .isSystemConditionProviderEnabled(ZenModeConfig.SCHEDULE_PATH); 117 | } 118 | 119 | private ZenModeConfig getZenModeConfig() { 120 | return NotificationManager.from(mContext).getZenModeConfig(); 121 | } 122 | 123 | private final class SettingsObserver extends ContentObserver { 124 | private final Uri ZEN_MODE_URI = Global.getUriFor(Global.ZEN_MODE); 125 | private final Uri ZEN_MODE_CONFIG_ETAG_URI = Global.getUriFor(Global.ZEN_MODE_CONFIG_ETAG); 126 | 127 | private SettingsObserver() { 128 | super(mHandler); 129 | } 130 | 131 | public void register() { 132 | getContentResolver().registerContentObserver(ZEN_MODE_URI, false, this); 133 | getContentResolver().registerContentObserver(ZEN_MODE_CONFIG_ETAG_URI, false, this); 134 | } 135 | 136 | public void unregister() { 137 | getContentResolver().unregisterContentObserver(this); 138 | } 139 | 140 | @Override 141 | public void onChange(boolean selfChange, Uri uri) { 142 | super.onChange(selfChange, uri); 143 | if (ZEN_MODE_URI.equals(uri)) { 144 | updateZenMode(true /*fireChanged*/); 145 | } 146 | if (ZEN_MODE_CONFIG_ETAG_URI.equals(uri)) { 147 | updateZenModeConfig(true /*fireChanged*/); 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /notification/SettingPref.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.content.ContentResolver; 20 | import android.content.Context; 21 | import android.content.res.Resources; 22 | import android.net.Uri; 23 | import android.preference.Preference; 24 | import android.preference.TwoStatePreference; 25 | import android.preference.Preference.OnPreferenceChangeListener; 26 | import android.provider.Settings.Global; 27 | import android.provider.Settings.System; 28 | 29 | import com.android.settings.DropDownPreference; 30 | import com.android.settings.SettingsPreferenceFragment; 31 | 32 | /** Helper to manage a two-state or dropdown preference bound to a global or system setting. */ 33 | public class SettingPref { 34 | public static final int TYPE_GLOBAL = 1; 35 | public static final int TYPE_SYSTEM = 2; 36 | 37 | protected final int mType; 38 | private final String mKey; 39 | protected final String mSetting; 40 | protected final int mDefault; 41 | private final int[] mValues; 42 | private final Uri mUri; 43 | 44 | protected TwoStatePreference mTwoState; 45 | protected DropDownPreference mDropDown; 46 | 47 | public SettingPref(int type, String key, String setting, int def, int... values) { 48 | mType = type; 49 | mKey = key; 50 | mSetting = setting; 51 | mDefault = def; 52 | mValues = values; 53 | mUri = getUriFor(mType, mSetting); 54 | } 55 | 56 | public boolean isApplicable(Context context) { 57 | return true; 58 | } 59 | 60 | protected String getCaption(Resources res, int value) { 61 | throw new UnsupportedOperationException(); 62 | } 63 | 64 | public Preference init(SettingsPreferenceFragment settings) { 65 | final Context context = settings.getActivity(); 66 | Preference p = settings.getPreferenceScreen().findPreference(mKey); 67 | if (p != null && !isApplicable(context)) { 68 | settings.getPreferenceScreen().removePreference(p); 69 | p = null; 70 | } 71 | if (p instanceof TwoStatePreference) { 72 | mTwoState = (TwoStatePreference) p; 73 | } else if (p instanceof DropDownPreference) { 74 | mDropDown = (DropDownPreference) p; 75 | for (int value : mValues) { 76 | mDropDown.addItem(getCaption(context.getResources(), value), value); 77 | } 78 | } 79 | update(context); 80 | if (mTwoState != null) { 81 | p.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 82 | @Override 83 | public boolean onPreferenceChange(Preference preference, Object newValue) { 84 | setSetting(context, (Boolean) newValue ? 1 : 0); 85 | return true; 86 | } 87 | }); 88 | return mTwoState; 89 | } 90 | if (mDropDown != null) { 91 | mDropDown.setCallback(new DropDownPreference.Callback() { 92 | @Override 93 | public boolean onItemSelected(int pos, Object value) { 94 | return setSetting(context, (Integer) value); 95 | } 96 | }); 97 | return mDropDown; 98 | } 99 | return null; 100 | } 101 | 102 | protected boolean setSetting(Context context, int value) { 103 | return putInt(mType, context.getContentResolver(), mSetting, value); 104 | } 105 | 106 | public Uri getUri() { 107 | return mUri; 108 | } 109 | 110 | public String getKey() { 111 | return mKey; 112 | } 113 | 114 | public void update(Context context) { 115 | final int val = getInt(mType, context.getContentResolver(), mSetting, mDefault); 116 | if (mTwoState != null) { 117 | mTwoState.setChecked(val != 0); 118 | } else if (mDropDown != null) { 119 | mDropDown.setSelectedValue(val); 120 | } 121 | } 122 | 123 | private static Uri getUriFor(int type, String setting) { 124 | switch(type) { 125 | case TYPE_GLOBAL: 126 | return Global.getUriFor(setting); 127 | case TYPE_SYSTEM: 128 | return System.getUriFor(setting); 129 | } 130 | throw new IllegalArgumentException(); 131 | } 132 | 133 | protected static boolean putInt(int type, ContentResolver cr, String setting, int value) { 134 | switch(type) { 135 | case TYPE_GLOBAL: 136 | return Global.putInt(cr, setting, value); 137 | case TYPE_SYSTEM: 138 | return System.putInt(cr, setting, value); 139 | } 140 | throw new IllegalArgumentException(); 141 | } 142 | 143 | protected static int getInt(int type, ContentResolver cr, String setting, int def) { 144 | switch(type) { 145 | case TYPE_GLOBAL: 146 | return Global.getInt(cr, setting, def); 147 | case TYPE_SYSTEM: 148 | return System.getInt(cr, setting, def); 149 | } 150 | throw new IllegalArgumentException(); 151 | } 152 | } -------------------------------------------------------------------------------- /notification/ZenModeExternalRuleSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.app.Activity; 20 | import android.content.ComponentName; 21 | import android.content.Intent; 22 | import android.content.pm.ServiceInfo; 23 | import android.net.Uri; 24 | import android.preference.Preference; 25 | import android.preference.Preference.OnPreferenceClickListener; 26 | import android.preference.PreferenceScreen; 27 | import android.provider.Settings; 28 | import android.service.notification.ZenModeConfig.ZenRule; 29 | import android.util.Log; 30 | 31 | import com.android.internal.logging.MetricsLogger; 32 | import com.android.settings.R; 33 | import com.android.settings.notification.ZenRuleNameDialog.RuleInfo; 34 | 35 | public class ZenModeExternalRuleSettings extends ZenModeRuleSettingsBase { 36 | private static final String KEY_TYPE = "type"; 37 | private static final String KEY_CONFIGURE = "configure"; 38 | 39 | public static final String ACTION = Settings.ACTION_ZEN_MODE_EXTERNAL_RULE_SETTINGS; 40 | private static final int REQUEST_CODE_CONFIGURE = 1; 41 | 42 | private static final String MD_RULE_TYPE = "automatic.ruleType"; 43 | private static final String MD_DEFAULT_CONDITION_ID = "automatic.defaultConditionId"; 44 | private static final String MD_CONFIGURATION_ACTIVITY = "automatic.configurationActivity"; 45 | private static final String EXTRA_CONDITION_ID = "automatic.conditionId"; 46 | 47 | private Preference mType; 48 | private Preference mConfigure; 49 | 50 | @Override 51 | protected boolean setRule(ZenRule rule) { 52 | return rule != null; 53 | } 54 | 55 | @Override 56 | protected String getZenModeDependency() { 57 | return null; 58 | } 59 | 60 | @Override 61 | protected int getEnabledToastText() { 62 | return 0; 63 | } 64 | 65 | @Override 66 | protected void onCreateInternal() { 67 | addPreferencesFromResource(R.xml.zen_mode_external_rule_settings); 68 | final PreferenceScreen root = getPreferenceScreen(); 69 | final ServiceInfo si = ServiceListing.findService(mContext, 70 | ZenModeAutomationSettings.CONFIG, mRule.component); 71 | if (DEBUG) Log.d(TAG, "ServiceInfo: " + si); 72 | final RuleInfo ri = getRuleInfo(si); 73 | if (DEBUG) Log.d(TAG, "RuleInfo: " + ri); 74 | mType = root.findPreference(KEY_TYPE); 75 | if (ri == null) { 76 | mType.setSummary(R.string.zen_mode_rule_type_unknown); 77 | } else { 78 | mType.setSummary(ri.caption); 79 | } 80 | 81 | mConfigure = root.findPreference(KEY_CONFIGURE); 82 | if (ri == null || ri.configurationActivity == null) { 83 | mConfigure.setEnabled(false); 84 | } else { 85 | mConfigure.setOnPreferenceClickListener(new OnPreferenceClickListener() { 86 | @Override 87 | public boolean onPreferenceClick(Preference preference) { 88 | startActivityForResult(new Intent().setComponent(ri.configurationActivity), 89 | REQUEST_CODE_CONFIGURE); 90 | return true; 91 | } 92 | }); 93 | } 94 | } 95 | 96 | @Override 97 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 98 | super.onActivityResult(requestCode, resultCode, data); 99 | if (requestCode == REQUEST_CODE_CONFIGURE) { 100 | if (resultCode == Activity.RESULT_OK && data != null) { 101 | final Uri conditionId = data.getParcelableExtra(EXTRA_CONDITION_ID); 102 | if (conditionId != null && !conditionId.equals(mRule.conditionId)) { 103 | updateRule(conditionId); 104 | } 105 | } 106 | } 107 | } 108 | 109 | public static RuleInfo getRuleInfo(ServiceInfo si) { 110 | if (si == null || si.metaData == null) return null; 111 | final String ruleType = si.metaData.getString(MD_RULE_TYPE); 112 | final String defaultConditionId = si.metaData.getString(MD_DEFAULT_CONDITION_ID); 113 | final String configurationActivity = si.metaData.getString(MD_CONFIGURATION_ACTIVITY); 114 | if (ruleType != null && !ruleType.trim().isEmpty() && defaultConditionId != null) { 115 | final RuleInfo ri = new RuleInfo(); 116 | ri.serviceComponent = new ComponentName(si.packageName, si.name); 117 | ri.settingsAction = ZenModeExternalRuleSettings.ACTION; 118 | ri.caption = ruleType; 119 | ri.defaultConditionId = Uri.parse(defaultConditionId); 120 | if (configurationActivity != null) { 121 | ri.configurationActivity = ComponentName.unflattenFromString(configurationActivity); 122 | } 123 | return ri; 124 | } 125 | return null; 126 | } 127 | 128 | @Override 129 | protected void updateControlsInternal() { 130 | // everything done up front 131 | } 132 | 133 | @Override 134 | protected int getMetricsCategory() { 135 | return MetricsLogger.NOTIFICATION_ZEN_MODE_EXTERNAL_RULE; 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /notification/ZenModeSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * Copyright (C) 2016 The CyanogenMod Project 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.android.settings.notification; 19 | 20 | import android.content.Context; 21 | import android.content.res.Resources; 22 | import android.os.Bundle; 23 | import android.preference.Preference; 24 | import android.preference.PreferenceScreen; 25 | import android.util.SparseArray; 26 | 27 | import com.android.internal.logging.MetricsLogger; 28 | import com.android.settings.R; 29 | import com.android.settings.Utils; 30 | import com.android.settings.search.BaseSearchIndexProvider; 31 | import com.android.settings.search.Indexable; 32 | import com.android.settings.search.SearchIndexableRaw; 33 | 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | 37 | public class ZenModeSettings extends ZenModeSettingsBase implements Indexable { 38 | private static final String KEY_PRIORITY_SETTINGS = "priority_settings"; 39 | private static final String KEY_AUTOMATION_SETTINGS = "automation_settings"; 40 | private static final String KEY_ZEN_ACCESS = "manage_zen_access"; 41 | 42 | private Preference mPrioritySettings; 43 | private Preference mZenAccess; 44 | 45 | @Override 46 | public void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | 49 | addPreferencesFromResource(R.xml.zen_mode_settings); 50 | final PreferenceScreen root = getPreferenceScreen(); 51 | 52 | mPrioritySettings = root.findPreference(KEY_PRIORITY_SETTINGS); 53 | if (!isScheduleSupported(mContext)) { 54 | removePreference(KEY_AUTOMATION_SETTINGS); 55 | } 56 | 57 | mZenAccess = findPreference(KEY_ZEN_ACCESS); 58 | refreshZenAccess(); 59 | } 60 | 61 | @Override 62 | public void onResume() { 63 | super.onResume(); 64 | updateControls(); 65 | refreshZenAccess(); 66 | } 67 | 68 | @Override 69 | protected int getMetricsCategory() { 70 | return MetricsLogger.NOTIFICATION_ZEN_MODE; 71 | } 72 | 73 | @Override 74 | protected void onZenModeChanged() { 75 | updateControls(); 76 | } 77 | 78 | @Override 79 | protected void onZenModeConfigChanged() { 80 | updateControls(); 81 | } 82 | 83 | private void updateControls() { 84 | updatePrioritySettingsSummary(); 85 | } 86 | 87 | private void updatePrioritySettingsSummary() { 88 | final ArrayList items = new ArrayList<>(); 89 | items.add(getString(R.string.zen_mode_alarms)); 90 | if (mConfig.allowReminders) { 91 | items.add(getString(R.string.zen_mode_summary_reminders)); 92 | } 93 | if (mConfig.allowEvents) { 94 | items.add(getString(R.string.zen_mode_summary_events)); 95 | } 96 | if (mConfig.allowCalls || mConfig.allowRepeatCallers) { 97 | items.add(getString(R.string.zen_mode_summary_selected_callers)); 98 | } 99 | if (mConfig.allowMessages) { 100 | items.add(getString(R.string.zen_mode_summary_selected_messages)); 101 | } 102 | mPrioritySettings.setSummary(Utils.join(getResources(), items)); 103 | } 104 | 105 | private static SparseArray allKeyTitles(Context context) { 106 | final SparseArray rt = new SparseArray(); 107 | rt.put(R.string.zen_mode_priority_settings_title, KEY_PRIORITY_SETTINGS); 108 | rt.put(R.string.zen_mode_automation_settings_title, KEY_AUTOMATION_SETTINGS); 109 | return rt; 110 | } 111 | 112 | @Override 113 | protected int getHelpResource() { 114 | return R.string.help_uri_interruptions; 115 | } 116 | 117 | // Enable indexing of searchable data 118 | public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 119 | new BaseSearchIndexProvider() { 120 | 121 | @Override 122 | public List getRawDataToIndex(Context context, boolean enabled) { 123 | final SparseArray keyTitles = allKeyTitles(context); 124 | final int N = keyTitles.size(); 125 | final List result = new ArrayList(N); 126 | final Resources res = context.getResources(); 127 | for (int i = 0; i < N; i++) { 128 | final SearchIndexableRaw data = new SearchIndexableRaw(context); 129 | data.key = keyTitles.valueAt(i); 130 | data.title = res.getString(keyTitles.keyAt(i)); 131 | data.screenTitle = res.getString(R.string.zen_mode_settings_title); 132 | result.add(data); 133 | } 134 | return result; 135 | } 136 | 137 | @Override 138 | public List getNonIndexableKeys(Context context) { 139 | final ArrayList rt = new ArrayList(); 140 | if (!isScheduleSupported(context)) { 141 | rt.add(KEY_AUTOMATION_SETTINGS); 142 | } 143 | return rt; 144 | } 145 | }; 146 | 147 | // === Zen access === 148 | 149 | private void refreshZenAccess() { 150 | // noop for now 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /notification/NotificationManagerSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The CyanogenMod project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.content.Context; 20 | import android.os.Bundle; 21 | import android.os.UserHandle; 22 | import android.preference.PreferenceCategory; 23 | import android.provider.SearchIndexableResource; 24 | import android.provider.Settings; 25 | import android.util.Log; 26 | 27 | import com.android.internal.logging.MetricsLogger; 28 | import com.android.internal.widget.LockPatternUtils; 29 | import com.android.settings.DropDownPreference; 30 | import com.android.settings.R; 31 | import com.android.settings.SettingsPreferenceFragment; 32 | import com.android.settings.search.BaseSearchIndexProvider; 33 | import com.android.settings.search.Indexable; 34 | import org.cyanogenmod.internal.logging.CMMetricsLogger; 35 | 36 | import java.util.ArrayList; 37 | import java.util.List; 38 | 39 | public class NotificationManagerSettings extends SettingsPreferenceFragment 40 | implements Indexable { 41 | 42 | private static final String TAG = NotificationManagerSettings.class.getSimpleName(); 43 | 44 | private static final String KEY_LOCK_SCREEN_NOTIFICATIONS = "lock_screen_notifications"; 45 | 46 | private boolean mSecure; 47 | private int mLockscreenSelectedValue; 48 | private DropDownPreference mLockscreen; 49 | 50 | @Override 51 | public void onCreate(Bundle icicle) { 52 | super.onCreate(icicle); 53 | addPreferencesFromResource(R.xml.notification_manager_settings); 54 | mSecure = new LockPatternUtils(getActivity()).isSecure(UserHandle.myUserId()); 55 | initLockscreenNotifications(); 56 | } 57 | 58 | // === Lockscreen (public / private) notifications === 59 | 60 | private void initLockscreenNotifications() { 61 | mLockscreen = (DropDownPreference) findPreference(KEY_LOCK_SCREEN_NOTIFICATIONS); 62 | if (mLockscreen == null) { 63 | Log.i(TAG, "Preference not found: " + KEY_LOCK_SCREEN_NOTIFICATIONS); 64 | return; 65 | } 66 | 67 | mLockscreen.addItem(R.string.lock_screen_notifications_summary_show, 68 | R.string.lock_screen_notifications_summary_show); 69 | if (mSecure) { 70 | mLockscreen.addItem(R.string.lock_screen_notifications_summary_hide, 71 | R.string.lock_screen_notifications_summary_hide); 72 | } 73 | mLockscreen.addItem(R.string.lock_screen_notifications_summary_disable, 74 | R.string.lock_screen_notifications_summary_disable); 75 | updateLockscreenNotifications(); 76 | mLockscreen.setCallback(new DropDownPreference.Callback() { 77 | @Override 78 | public boolean onItemSelected(int pos, Object value) { 79 | final int val = (Integer) value; 80 | if (val == mLockscreenSelectedValue) { 81 | return true; 82 | } 83 | final boolean enabled = val != R.string.lock_screen_notifications_summary_disable; 84 | final boolean show = val == R.string.lock_screen_notifications_summary_show; 85 | Settings.Secure.putInt(getContentResolver(), 86 | Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, show ? 1 : 0); 87 | Settings.Secure.putInt(getContentResolver(), 88 | Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, enabled ? 1 : 0); 89 | mLockscreenSelectedValue = val; 90 | return true; 91 | } 92 | }); 93 | } 94 | 95 | private void updateLockscreenNotifications() { 96 | if (mLockscreen == null) { 97 | return; 98 | } 99 | final boolean enabled = getLockscreenNotificationsEnabled(); 100 | final boolean allowPrivate = !mSecure || getLockscreenAllowPrivateNotifications(); 101 | mLockscreenSelectedValue = !enabled ? R.string.lock_screen_notifications_summary_disable : 102 | allowPrivate ? R.string.lock_screen_notifications_summary_show : 103 | R.string.lock_screen_notifications_summary_hide; 104 | mLockscreen.setSelectedValue(mLockscreenSelectedValue); 105 | } 106 | 107 | private boolean getLockscreenNotificationsEnabled() { 108 | return Settings.Secure.getInt(getContentResolver(), 109 | Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0; 110 | } 111 | 112 | private boolean getLockscreenAllowPrivateNotifications() { 113 | return Settings.Secure.getInt(getContentResolver(), 114 | Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0; 115 | } 116 | 117 | public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 118 | new BaseSearchIndexProvider() { 119 | @Override 120 | public List getXmlResourcesToIndex(Context context, 121 | boolean enabled) { 122 | ArrayList result = 123 | new ArrayList(); 124 | 125 | SearchIndexableResource sir = new SearchIndexableResource(context); 126 | sir.xmlResId = R.xml.notification_manager_settings; 127 | result.add(sir); 128 | 129 | return result; 130 | } 131 | }; 132 | 133 | @Override 134 | protected int getMetricsCategory() { 135 | return CMMetricsLogger.NOTIFICATION_MANAGER_SETTINGS; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /notification/NotificationBackend.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.android.settings.notification; 17 | 18 | import android.app.INotificationManager; 19 | import android.app.Notification; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.content.pm.ApplicationInfo; 23 | import android.content.pm.PackageManager; 24 | import android.graphics.drawable.Drawable; 25 | import android.os.ServiceManager; 26 | import android.service.notification.NotificationListenerService; 27 | import android.util.Log; 28 | 29 | public class NotificationBackend { 30 | private static final String TAG = "NotificationBackend"; 31 | 32 | static INotificationManager sINM = INotificationManager.Stub.asInterface( 33 | ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 34 | 35 | public AppRow loadAppRow(PackageManager pm, ApplicationInfo app) { 36 | final AppRow row = new AppRow(); 37 | row.pkg = app.packageName; 38 | row.uid = app.uid; 39 | try { 40 | row.label = app.loadLabel(pm); 41 | } catch (Throwable t) { 42 | Log.e(TAG, "Error loading application label for " + row.pkg, t); 43 | row.label = row.pkg; 44 | } 45 | row.icon = app.loadIcon(pm); 46 | row.banned = getNotificationsBanned(row.pkg, row.uid); 47 | row.priority = getHighPriority(row.pkg, row.uid); 48 | row.peekable = getPeekable(row.pkg, row.uid); 49 | row.sensitive = getSensitive(row.pkg, row.uid); 50 | return row; 51 | } 52 | 53 | public boolean setNotificationsBanned(String pkg, int uid, boolean banned) { 54 | try { 55 | sINM.setNotificationsEnabledForPackage(pkg, uid, !banned); 56 | return true; 57 | } catch (Exception e) { 58 | Log.w(TAG, "Error calling NoMan", e); 59 | return false; 60 | } 61 | } 62 | 63 | public boolean getNotificationsBanned(String pkg, int uid) { 64 | try { 65 | final boolean enabled = sINM.areNotificationsEnabledForPackage(pkg, uid); 66 | return !enabled; 67 | } catch (Exception e) { 68 | Log.w(TAG, "Error calling NoMan", e); 69 | return false; 70 | } 71 | } 72 | 73 | public boolean getHighPriority(String pkg, int uid) { 74 | try { 75 | return sINM.getPackagePriority(pkg, uid) == Notification.PRIORITY_MAX; 76 | } catch (Exception e) { 77 | Log.w(TAG, "Error calling NoMan", e); 78 | return false; 79 | } 80 | } 81 | 82 | public boolean setHighPriority(String pkg, int uid, boolean highPriority) { 83 | try { 84 | sINM.setPackagePriority(pkg, uid, 85 | highPriority ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT); 86 | return true; 87 | } catch (Exception e) { 88 | Log.w(TAG, "Error calling NoMan", e); 89 | return false; 90 | } 91 | } 92 | 93 | public boolean getPeekable(String pkg, int uid) { 94 | try { 95 | return sINM.getPackagePeekable(pkg, uid); 96 | } catch (Exception e) { 97 | Log.w(TAG, "Error calling NoMan", e); 98 | return false; 99 | } 100 | } 101 | 102 | public boolean setPeekable(String pkg, int uid, boolean peekable) { 103 | try { 104 | sINM.setPackagePeekable(pkg, uid, peekable); 105 | return true; 106 | } catch (Exception e) { 107 | Log.w(TAG, "Error calling NoMan", e); 108 | return false; 109 | } 110 | } 111 | 112 | public boolean getSensitive(String pkg, int uid) { 113 | try { 114 | return sINM.getPackageVisibilityOverride(pkg, uid) == Notification.VISIBILITY_PRIVATE; 115 | } catch (Exception e) { 116 | Log.w(TAG, "Error calling NoMan", e); 117 | return false; 118 | } 119 | } 120 | 121 | public boolean setSensitive(String pkg, int uid, boolean sensitive) { 122 | try { 123 | sINM.setPackageVisibilityOverride(pkg, uid, 124 | sensitive ? Notification.VISIBILITY_PRIVATE 125 | : NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE); 126 | return true; 127 | } catch (Exception e) { 128 | Log.w(TAG, "Error calling NoMan", e); 129 | return false; 130 | } 131 | } 132 | 133 | public int getShowNotificationForPackageOnKeyguard(String pkg, int uid) { 134 | try { 135 | return sINM.getShowNotificationForPackageOnKeyguard(pkg, uid); 136 | } catch (Exception e) { 137 | Log.w(TAG, "Error calling NoMan", e); 138 | return Notification.SHOW_ALL_NOTI_ON_KEYGUARD; 139 | } 140 | } 141 | 142 | public boolean setShowNotificationForPackageOnKeyguard(String pkg, int uid, int status) { 143 | try { 144 | sINM.setShowNotificationForPackageOnKeyguard(pkg, uid, status); 145 | return true; 146 | } catch (Exception e) { 147 | Log.w(TAG, "Error calling NoMan", e); 148 | return false; 149 | } 150 | } 151 | 152 | static class Row { 153 | public String section; 154 | } 155 | 156 | public static class AppRow extends Row { 157 | public String pkg; 158 | public int uid; 159 | public Drawable icon; 160 | public CharSequence label; 161 | public Intent settingsIntent; 162 | public boolean banned; 163 | public boolean priority; 164 | public boolean peekable; 165 | public boolean sensitive; 166 | public boolean first; // first app in section 167 | } 168 | 169 | } 170 | -------------------------------------------------------------------------------- /notification/ZenModeConditionSelection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.animation.LayoutTransition; 20 | import android.app.INotificationManager; 21 | import android.content.Context; 22 | import android.os.Handler; 23 | import android.os.Message; 24 | import android.os.RemoteException; 25 | import android.os.ServiceManager; 26 | import android.os.UserHandle; 27 | import android.service.notification.Condition; 28 | import android.service.notification.IConditionListener; 29 | import android.service.notification.ZenModeConfig; 30 | import android.text.TextUtils; 31 | import android.util.Log; 32 | import android.widget.CompoundButton; 33 | import android.widget.RadioButton; 34 | import android.widget.RadioGroup; 35 | 36 | import com.android.settings.R; 37 | 38 | import java.util.ArrayList; 39 | import java.util.List; 40 | 41 | public class ZenModeConditionSelection extends RadioGroup { 42 | private static final String TAG = "ZenModeConditionSelection"; 43 | private static final boolean DEBUG = true; 44 | 45 | private final INotificationManager mNoMan; 46 | private final H mHandler = new H(); 47 | private final Context mContext; 48 | private final List mConditions; 49 | private final int mZenMode; 50 | 51 | private Condition mCondition; 52 | 53 | public ZenModeConditionSelection(Context context, int zenMode) { 54 | super(context); 55 | mContext = context; 56 | mZenMode = zenMode; 57 | mConditions = new ArrayList(); 58 | setLayoutTransition(new LayoutTransition()); 59 | final int p = mContext.getResources().getDimensionPixelSize(R.dimen.content_margin_left); 60 | setPadding(p, p, p, 0); 61 | mNoMan = INotificationManager.Stub.asInterface( 62 | ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 63 | final RadioButton b = newRadioButton(null); 64 | b.setText(mContext.getString(com.android.internal.R.string.zen_mode_forever)); 65 | b.setChecked(true); 66 | for (int i = ZenModeConfig.MINUTE_BUCKETS.length - 1; i >= 0; --i) { 67 | handleCondition(ZenModeConfig.toTimeCondition(mContext, 68 | ZenModeConfig.MINUTE_BUCKETS[i], UserHandle.myUserId())); 69 | } 70 | } 71 | 72 | private RadioButton newRadioButton(Condition condition) { 73 | final RadioButton button = new RadioButton(mContext); 74 | button.setTag(condition); 75 | button.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 76 | @Override 77 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 78 | if (isChecked) { 79 | setCondition((Condition) button.getTag()); 80 | } 81 | } 82 | }); 83 | addView(button); 84 | return button; 85 | } 86 | 87 | @Override 88 | protected void onAttachedToWindow() { 89 | super.onAttachedToWindow(); 90 | requestZenModeConditions(Condition.FLAG_RELEVANT_NOW); 91 | } 92 | 93 | @Override 94 | protected void onDetachedFromWindow() { 95 | super.onDetachedFromWindow(); 96 | requestZenModeConditions(0 /*none*/); 97 | } 98 | 99 | protected void requestZenModeConditions(int relevance) { 100 | if (DEBUG) Log.d(TAG, "requestZenModeConditions " + Condition.relevanceToString(relevance)); 101 | try { 102 | mNoMan.requestZenModeConditions(mListener, relevance); 103 | } catch (RemoteException e) { 104 | // noop 105 | } 106 | } 107 | 108 | protected void handleConditions(Condition[] conditions) { 109 | for (Condition c : conditions) { 110 | handleCondition(c); 111 | } 112 | } 113 | 114 | protected void handleCondition(Condition c) { 115 | if (mConditions.contains(c)) return; 116 | RadioButton v = (RadioButton) findViewWithTag(c.id); 117 | if (c.state == Condition.STATE_TRUE || c.state == Condition.STATE_UNKNOWN) { 118 | if (v == null) { 119 | v = newRadioButton(c); 120 | } 121 | } 122 | if (v != null) { 123 | v.setText(computeConditionText(c)); 124 | v.setEnabled(c.state == Condition.STATE_TRUE); 125 | } 126 | mConditions.add(c); 127 | } 128 | 129 | protected void setCondition(Condition c) { 130 | if (DEBUG) Log.d(TAG, "setCondition " + c); 131 | mCondition = c; 132 | } 133 | 134 | public void confirmCondition() { 135 | if (DEBUG) Log.d(TAG, "confirmCondition " + mCondition); 136 | try { 137 | mNoMan.setZenMode(mZenMode, mCondition != null ? mCondition.id : null, TAG); 138 | } catch (RemoteException e) { 139 | // noop 140 | } 141 | } 142 | 143 | private static String computeConditionText(Condition c) { 144 | return !TextUtils.isEmpty(c.line1) ? c.line1 145 | : !TextUtils.isEmpty(c.summary) ? c.summary 146 | : ""; 147 | } 148 | 149 | private final IConditionListener mListener = new IConditionListener.Stub() { 150 | @Override 151 | public void onConditionsReceived(Condition[] conditions) { 152 | if (conditions == null || conditions.length == 0) return; 153 | mHandler.obtainMessage(H.CONDITIONS, conditions).sendToTarget(); 154 | } 155 | }; 156 | 157 | private final class H extends Handler { 158 | private static final int CONDITIONS = 1; 159 | 160 | @Override 161 | public void handleMessage(Message msg) { 162 | if (msg.what == CONDITIONS) handleConditions((Condition[]) msg.obj); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /notification/RedactionInterstitial.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import com.android.internal.logging.MetricsLogger; 20 | import com.android.settings.R; 21 | import com.android.settings.SettingsActivity; 22 | import com.android.settings.SettingsPreferenceFragment; 23 | 24 | import android.app.admin.DevicePolicyManager; 25 | import android.content.Context; 26 | import android.content.Intent; 27 | import android.os.Bundle; 28 | import android.provider.Settings; 29 | import android.view.LayoutInflater; 30 | import android.view.View; 31 | import android.view.ViewGroup; 32 | import android.widget.RadioButton; 33 | import android.widget.RadioGroup; 34 | 35 | public class RedactionInterstitial extends SettingsActivity { 36 | 37 | @Override 38 | public Intent getIntent() { 39 | Intent modIntent = new Intent(super.getIntent()); 40 | modIntent.putExtra(EXTRA_SHOW_FRAGMENT, RedactionInterstitialFragment.class.getName()); 41 | return modIntent; 42 | } 43 | 44 | @Override 45 | protected boolean isValidFragment(String fragmentName) { 46 | return RedactionInterstitialFragment.class.getName().equals(fragmentName); 47 | } 48 | 49 | /** 50 | * Create an intent for launching RedactionInterstitial. 51 | * @return An intent to launch the activity is if is available, @null if the activity is not 52 | * available to be launched. 53 | */ 54 | public static Intent createStartIntent(Context ctx) { 55 | if (isSecureNotificationsDisabled(ctx)) { 56 | // If there is no choices for the user, we should not start the activity. 57 | return null; 58 | } else { 59 | return new Intent(ctx, RedactionInterstitial.class) 60 | .putExtra(EXTRA_PREFS_SHOW_BUTTON_BAR, true) 61 | .putExtra(EXTRA_PREFS_SET_BACK_TEXT, (String) null) 62 | .putExtra(EXTRA_PREFS_SET_NEXT_TEXT, ctx.getString( 63 | R.string.app_notifications_dialog_done)) 64 | .putExtra(EXTRA_SHOW_FRAGMENT_TITLE_RESID, 65 | R.string.lock_screen_notifications_interstitial_title); 66 | } 67 | } 68 | 69 | private static boolean isSecureNotificationsDisabled(Context context) { 70 | final DevicePolicyManager dpm = 71 | (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 72 | return dpm != null && (dpm.getKeyguardDisabledFeatures(null) 73 | & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) != 0; 74 | } 75 | 76 | private static boolean isUnredactedNotificationsDisabled(Context context) { 77 | final DevicePolicyManager dpm = 78 | (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); 79 | return dpm != null && (dpm.getKeyguardDisabledFeatures(null) 80 | & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) != 0; 81 | } 82 | 83 | public static class RedactionInterstitialFragment extends SettingsPreferenceFragment 84 | implements RadioGroup.OnCheckedChangeListener { 85 | 86 | private RadioGroup mRadioGroup; 87 | private RadioButton mShowAllButton; 88 | private RadioButton mRedactSensitiveButton; 89 | 90 | @Override 91 | protected int getMetricsCategory() { 92 | return MetricsLogger.NOTIFICATION_REDACTION; 93 | } 94 | 95 | @Override 96 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 97 | Bundle savedInstanceState) { 98 | return inflater.inflate(R.layout.redaction_interstitial, container, false); 99 | } 100 | 101 | @Override 102 | public void onViewCreated(View view, Bundle savedInstanceState) { 103 | super.onViewCreated(view, savedInstanceState); 104 | mRadioGroup = (RadioGroup) view.findViewById(R.id.radio_group); 105 | mShowAllButton = (RadioButton) view.findViewById(R.id.show_all); 106 | mRedactSensitiveButton = (RadioButton) view.findViewById(R.id.redact_sensitive); 107 | 108 | mRadioGroup.setOnCheckedChangeListener(this); 109 | 110 | // Disable buttons according to policy. 111 | if (isSecureNotificationsDisabled(getActivity())) { 112 | mShowAllButton.setEnabled(false); 113 | mRedactSensitiveButton.setEnabled(false); 114 | } else if (isUnredactedNotificationsDisabled(getActivity())) { 115 | mShowAllButton.setEnabled(false); 116 | } 117 | } 118 | 119 | @Override 120 | public void onResume() { 121 | super.onResume(); 122 | loadFromSettings(); 123 | } 124 | 125 | private void loadFromSettings() { 126 | final boolean enabled = Settings.Secure.getInt(getContentResolver(), 127 | Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0; 128 | final boolean show = Settings.Secure.getInt(getContentResolver(), 129 | Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1) != 0; 130 | 131 | int checkedButtonId = R.id.hide_all; 132 | if (enabled) { 133 | if (show && mShowAllButton.isEnabled()) { 134 | checkedButtonId = R.id.show_all; 135 | } else if (mRedactSensitiveButton.isEnabled()) { 136 | checkedButtonId = R.id.redact_sensitive; 137 | } 138 | } 139 | 140 | mRadioGroup.check(checkedButtonId); 141 | } 142 | 143 | @Override 144 | public void onCheckedChanged(RadioGroup group, int checkedId) { 145 | final boolean show = (checkedId == R.id.show_all); 146 | final boolean enabled = (checkedId != R.id.hide_all); 147 | 148 | Settings.Secure.putInt(getContentResolver(), 149 | Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, show ? 1 : 0); 150 | Settings.Secure.putInt(getContentResolver(), 151 | Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, enabled ? 1 : 0); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /notification/ManagedServiceSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.app.AlertDialog; 20 | import android.app.Dialog; 21 | import android.app.DialogFragment; 22 | import android.content.ComponentName; 23 | import android.content.Context; 24 | import android.content.DialogInterface; 25 | import android.content.pm.PackageItemInfo; 26 | import android.content.pm.PackageManager; 27 | import android.content.pm.ServiceInfo; 28 | import android.os.Bundle; 29 | import android.preference.Preference; 30 | import android.preference.Preference.OnPreferenceChangeListener; 31 | import android.preference.PreferenceScreen; 32 | import android.preference.SwitchPreference; 33 | import android.view.LayoutInflater; 34 | import android.view.View; 35 | import android.view.ViewGroup; 36 | import android.widget.ListView; 37 | import android.widget.TextView; 38 | 39 | import com.android.settings.R; 40 | import com.android.settings.SettingsPreferenceFragment; 41 | 42 | import java.util.Collections; 43 | import java.util.List; 44 | 45 | public abstract class ManagedServiceSettings extends SettingsPreferenceFragment { 46 | private final Config mConfig; 47 | 48 | private Context mContext; 49 | private PackageManager mPM; 50 | private ServiceListing mServiceListing; 51 | private TextView mEmpty; 52 | 53 | abstract protected Config getConfig(); 54 | 55 | public ManagedServiceSettings() { 56 | mConfig = getConfig(); 57 | } 58 | 59 | @Override 60 | public void onCreate(Bundle icicle) { 61 | super.onCreate(icicle); 62 | 63 | mContext = getActivity(); 64 | mPM = mContext.getPackageManager(); 65 | mServiceListing = new ServiceListing(mContext, mConfig); 66 | mServiceListing.addCallback(new ServiceListing.Callback() { 67 | @Override 68 | public void onServicesReloaded(List services) { 69 | updateList(services); 70 | } 71 | }); 72 | setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext)); 73 | } 74 | 75 | @Override 76 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 77 | Bundle savedInstanceState) { 78 | final View v = inflater.inflate(R.layout.managed_service_settings, container, false); 79 | mEmpty = (TextView) v.findViewById(android.R.id.empty); 80 | mEmpty.setText(mConfig.emptyText); 81 | ((ListView) v.findViewById(android.R.id.list)).setEmptyView(mEmpty); 82 | return v; 83 | } 84 | 85 | @Override 86 | public void onResume() { 87 | super.onResume(); 88 | mServiceListing.reload(); 89 | mServiceListing.setListening(true); 90 | } 91 | 92 | @Override 93 | public void onPause() { 94 | super.onPause(); 95 | mServiceListing.setListening(false); 96 | } 97 | 98 | private void updateList(List services) { 99 | final PreferenceScreen screen = getPreferenceScreen(); 100 | screen.removeAll(); 101 | Collections.sort(services, new PackageItemInfo.DisplayNameComparator(mPM)); 102 | for (ServiceInfo service : services) { 103 | final ComponentName cn = new ComponentName(service.packageName, service.name); 104 | final String title = service.loadLabel(mPM).toString(); 105 | final SwitchPreference pref = new SwitchPreference(mContext); 106 | pref.setPersistent(false); 107 | pref.setIcon(service.loadIcon(mPM)); 108 | pref.setTitle(title); 109 | pref.setChecked(mServiceListing.isEnabled(cn)); 110 | pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 111 | @Override 112 | public boolean onPreferenceChange(Preference preference, Object newValue) { 113 | final boolean enable = (boolean) newValue; 114 | return setEnabled(cn, title, enable); 115 | } 116 | }); 117 | screen.addPreference(pref); 118 | } 119 | } 120 | 121 | private boolean setEnabled(ComponentName service, String title, boolean enable) { 122 | if (!enable) { 123 | // the simple version: disabling 124 | mServiceListing.setEnabled(service, false); 125 | return true; 126 | } else { 127 | if (mServiceListing.isEnabled(service)) { 128 | return true; // already enabled 129 | } 130 | // show a scary dialog 131 | new ScaryWarningDialogFragment() 132 | .setServiceInfo(service, title) 133 | .show(getFragmentManager(), "dialog"); 134 | return false; 135 | } 136 | } 137 | 138 | public class ScaryWarningDialogFragment extends DialogFragment { 139 | static final String KEY_COMPONENT = "c"; 140 | static final String KEY_LABEL = "l"; 141 | 142 | public ScaryWarningDialogFragment setServiceInfo(ComponentName cn, String label) { 143 | Bundle args = new Bundle(); 144 | args.putString(KEY_COMPONENT, cn.flattenToString()); 145 | args.putString(KEY_LABEL, label); 146 | setArguments(args); 147 | return this; 148 | } 149 | 150 | @Override 151 | public Dialog onCreateDialog(Bundle savedInstanceState) { 152 | super.onCreate(savedInstanceState); 153 | final Bundle args = getArguments(); 154 | final String label = args.getString(KEY_LABEL); 155 | final ComponentName cn = ComponentName.unflattenFromString(args 156 | .getString(KEY_COMPONENT)); 157 | 158 | final String title = getResources().getString(mConfig.warningDialogTitle, label); 159 | final String summary = getResources().getString(mConfig.warningDialogSummary, label); 160 | return new AlertDialog.Builder(mContext) 161 | .setMessage(summary) 162 | .setTitle(title) 163 | .setCancelable(true) 164 | .setPositiveButton(R.string.allow, 165 | new DialogInterface.OnClickListener() { 166 | public void onClick(DialogInterface dialog, int id) { 167 | mServiceListing.setEnabled(cn, true); 168 | } 169 | }) 170 | .setNegativeButton(R.string.deny, 171 | new DialogInterface.OnClickListener() { 172 | public void onClick(DialogInterface dialog, int id) { 173 | // pass 174 | } 175 | }) 176 | .create(); 177 | } 178 | } 179 | 180 | protected static class Config { 181 | String tag; 182 | String setting; 183 | String intentAction; 184 | String permission; 185 | String noun; 186 | int warningDialogTitle; 187 | int warningDialogSummary; 188 | int emptyText; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /notification/VolumeSeekBarPreference.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.content.ContentResolver; 20 | import android.content.Context; 21 | import android.media.AudioManager; 22 | import android.net.Uri; 23 | import android.preference.PreferenceManager; 24 | import android.preference.SeekBarPreference; 25 | import android.preference.SeekBarVolumizer; 26 | import android.text.TextUtils; 27 | import android.util.AttributeSet; 28 | import android.util.Log; 29 | import android.view.View; 30 | import android.widget.ImageView; 31 | import android.widget.SeekBar; 32 | import android.widget.TextView; 33 | 34 | import com.android.settings.R; 35 | 36 | import java.util.Objects; 37 | 38 | /** A slider preference that directly controls an audio stream volume (no dialog) **/ 39 | public class VolumeSeekBarPreference extends SeekBarPreference 40 | implements PreferenceManager.OnActivityStopListener { 41 | private static final String TAG = "VolumeSeekBarPreference"; 42 | 43 | private int mStream; 44 | private SeekBar mSeekBar; 45 | private SeekBarVolumizer mVolumizer; 46 | private Callback mCallback; 47 | private ImageView mIconView; 48 | private TextView mSuppressionTextView; 49 | private String mSuppressionText; 50 | private boolean mMuted; 51 | private boolean mZenMuted; 52 | private int mIconResId; 53 | private int mMuteIconResId; 54 | private boolean mStopped; 55 | 56 | public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr, 57 | int defStyleRes) { 58 | super(context, attrs, defStyleAttr, defStyleRes); 59 | setLayoutResource(R.layout.preference_volume_slider); 60 | } 61 | 62 | public VolumeSeekBarPreference(Context context, AttributeSet attrs, int defStyleAttr) { 63 | this(context, attrs, defStyleAttr, 0); 64 | } 65 | 66 | public VolumeSeekBarPreference(Context context, AttributeSet attrs) { 67 | this(context, attrs, 0); 68 | } 69 | 70 | public VolumeSeekBarPreference(Context context) { 71 | this(context, null); 72 | } 73 | 74 | public void setStream(int stream) { 75 | mStream = stream; 76 | } 77 | 78 | public void setCallback(Callback callback) { 79 | mCallback = callback; 80 | } 81 | 82 | public void onActivityResume() { 83 | if (mStopped) { 84 | init(); 85 | } 86 | } 87 | 88 | @Override 89 | public void onActivityStop() { 90 | mStopped = true; 91 | if (mVolumizer != null) { 92 | mVolumizer.stop(); 93 | mVolumizer = null; 94 | } 95 | } 96 | 97 | @Override 98 | protected void onBindView(View view) { 99 | super.onBindView(view); 100 | if (mStream == 0) { 101 | Log.w(TAG, "No stream found, not binding volumizer"); 102 | return; 103 | } 104 | mSeekBar = (SeekBar) view.findViewById(com.android.internal.R.id.seekbar); 105 | mIconView = (ImageView) view.findViewById(com.android.internal.R.id.icon); 106 | mSuppressionTextView = (TextView) view.findViewById(R.id.suppression_text); 107 | init(); 108 | } 109 | 110 | private void init() { 111 | if (mSeekBar == null) return; 112 | getPreferenceManager().registerOnActivityStopListener(this); 113 | final SeekBarVolumizer.Callback sbvc = new SeekBarVolumizer.Callback() { 114 | @Override 115 | public void onSampleStarting(SeekBarVolumizer sbv) { 116 | if (mCallback != null) { 117 | mCallback.onSampleStarting(sbv); 118 | } 119 | } 120 | @Override 121 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { 122 | if (mCallback != null) { 123 | mCallback.onStreamValueChanged(mStream, progress); 124 | } 125 | } 126 | @Override 127 | public void onMuted(boolean muted, boolean zenMuted) { 128 | if (mMuted == muted && mZenMuted == zenMuted) return; 129 | mMuted = muted; 130 | mZenMuted = zenMuted; 131 | updateIconView(); 132 | } 133 | }; 134 | final Uri sampleUri = mStream == AudioManager.STREAM_MUSIC ? getMediaVolumeUri() : null; 135 | if (mVolumizer == null) { 136 | mVolumizer = new SeekBarVolumizer(getContext(), mStream, sampleUri, sbvc); 137 | } 138 | mVolumizer.start(); 139 | mVolumizer.setSeekBar(mSeekBar); 140 | updateIconView(); 141 | if (mCallback != null) { 142 | mCallback.onStreamValueChanged(mStream, mSeekBar.getProgress()); 143 | } 144 | updateSuppressionText(); 145 | if (!isEnabled()) { 146 | mSeekBar.setEnabled(false); 147 | mVolumizer.stop(); 148 | } 149 | } 150 | 151 | // during initialization, this preference is the SeekBar listener 152 | @Override 153 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { 154 | super.onProgressChanged(seekBar, progress, fromTouch); 155 | if (mCallback != null) { 156 | mCallback.onStreamValueChanged(mStream, progress); 157 | } 158 | } 159 | 160 | private void updateIconView() { 161 | if (mIconView == null) return; 162 | if (mIconResId != 0) { 163 | mIconView.setImageResource(mIconResId); 164 | } else if (mMuteIconResId != 0 && mMuted && !mZenMuted) { 165 | mIconView.setImageResource(mMuteIconResId); 166 | } else { 167 | mIconView.setImageDrawable(getIcon()); 168 | } 169 | } 170 | 171 | public void showIcon(int resId) { 172 | // Instead of using setIcon, which will trigger listeners, this just decorates the 173 | // preference temporarily with a new icon. 174 | if (mIconResId == resId) return; 175 | mIconResId = resId; 176 | updateIconView(); 177 | } 178 | 179 | public void setMuteIcon(int resId) { 180 | if (mMuteIconResId == resId) return; 181 | mMuteIconResId = resId; 182 | updateIconView(); 183 | } 184 | 185 | private Uri getMediaVolumeUri() { 186 | return Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" 187 | + getContext().getPackageName() 188 | + "/" + R.raw.media_volume); 189 | } 190 | 191 | public void setSuppressionText(String text) { 192 | if (Objects.equals(text, mSuppressionText)) return; 193 | mSuppressionText = text; 194 | updateSuppressionText(); 195 | } 196 | 197 | private void updateSuppressionText() { 198 | if (mSuppressionTextView != null && mSeekBar != null) { 199 | mSuppressionTextView.setText(mSuppressionText); 200 | final boolean showSuppression = !TextUtils.isEmpty(mSuppressionText); 201 | mSuppressionTextView.setVisibility(showSuppression ? View.VISIBLE : View.INVISIBLE); 202 | mSeekBar.setVisibility(showSuppression ? View.INVISIBLE : View.VISIBLE); 203 | } 204 | } 205 | 206 | public interface Callback { 207 | void onSampleStarting(SeekBarVolumizer sbv); 208 | void onStreamValueChanged(int stream, int progress); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /notification/ServiceListing.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.app.ActivityManager; 20 | import android.content.BroadcastReceiver; 21 | import android.content.ComponentName; 22 | import android.content.ContentResolver; 23 | import android.content.Context; 24 | import android.content.Intent; 25 | import android.content.IntentFilter; 26 | import android.content.pm.PackageManager; 27 | import android.content.pm.ResolveInfo; 28 | import android.content.pm.ServiceInfo; 29 | import android.database.ContentObserver; 30 | import android.net.Uri; 31 | import android.os.Handler; 32 | import android.provider.Settings; 33 | import android.util.Slog; 34 | 35 | import com.android.settings.notification.ManagedServiceSettings.Config; 36 | 37 | import java.util.ArrayList; 38 | import java.util.HashSet; 39 | import java.util.List; 40 | 41 | public class ServiceListing { 42 | private final ContentResolver mContentResolver; 43 | private final Context mContext; 44 | private final Config mConfig; 45 | private final HashSet mEnabledServices = new HashSet(); 46 | private final List mServices = new ArrayList(); 47 | private final List mCallbacks = new ArrayList(); 48 | 49 | private boolean mListening; 50 | 51 | public ServiceListing(Context context, Config config) { 52 | mContext = context; 53 | mConfig = config; 54 | mContentResolver = context.getContentResolver(); 55 | } 56 | 57 | public void addCallback(Callback callback) { 58 | mCallbacks.add(callback); 59 | } 60 | 61 | public void removeCallback(Callback callback) { 62 | mCallbacks.remove(callback); 63 | } 64 | 65 | public void setListening(boolean listening) { 66 | if (mListening == listening) return; 67 | mListening = listening; 68 | if (mListening) { 69 | // listen for package changes 70 | IntentFilter filter = new IntentFilter(); 71 | filter.addAction(Intent.ACTION_PACKAGE_ADDED); 72 | filter.addAction(Intent.ACTION_PACKAGE_CHANGED); 73 | filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 74 | filter.addAction(Intent.ACTION_PACKAGE_REPLACED); 75 | filter.addDataScheme("package"); 76 | mContext.registerReceiver(mPackageReceiver, filter); 77 | mContentResolver.registerContentObserver(Settings.Secure.getUriFor(mConfig.setting), 78 | false, mSettingsObserver); 79 | } else { 80 | mContext.unregisterReceiver(mPackageReceiver); 81 | mContentResolver.unregisterContentObserver(mSettingsObserver); 82 | } 83 | } 84 | 85 | public static int getEnabledServicesCount(Config config, Context context) { 86 | final String flat = Settings.Secure.getString(context.getContentResolver(), config.setting); 87 | if (flat == null || "".equals(flat)) return 0; 88 | final String[] components = flat.split(":"); 89 | return components.length; 90 | } 91 | 92 | public static int getServicesCount(Config c, PackageManager pm) { 93 | return getServices(c, null, pm); 94 | } 95 | 96 | public static ServiceInfo findService(Context context, Config config, final ComponentName cn) { 97 | final ServiceListing listing = new ServiceListing(context, config); 98 | final List services = listing.reload(); 99 | for (ServiceInfo service : services) { 100 | final ComponentName serviceCN = new ComponentName(service.packageName, service.name); 101 | if (serviceCN.equals(cn)) { 102 | return service; 103 | } 104 | } 105 | return null; 106 | } 107 | 108 | private static int getServices(Config c, List list, PackageManager pm) { 109 | int services = 0; 110 | if (list != null) { 111 | list.clear(); 112 | } 113 | final int user = ActivityManager.getCurrentUser(); 114 | 115 | List installedServices = pm.queryIntentServicesAsUser( 116 | new Intent(c.intentAction), 117 | PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, 118 | user); 119 | 120 | for (int i = 0, count = installedServices.size(); i < count; i++) { 121 | ResolveInfo resolveInfo = installedServices.get(i); 122 | ServiceInfo info = resolveInfo.serviceInfo; 123 | 124 | if (!c.permission.equals(info.permission)) { 125 | Slog.w(c.tag, "Skipping " + c.noun + " service " 126 | + info.packageName + "/" + info.name 127 | + ": it does not require the permission " 128 | + c.permission); 129 | continue; 130 | } 131 | if (list != null) { 132 | list.add(info); 133 | } 134 | services++; 135 | } 136 | return services; 137 | } 138 | 139 | private void saveEnabledServices() { 140 | StringBuilder sb = null; 141 | for (ComponentName cn : mEnabledServices) { 142 | if (sb == null) { 143 | sb = new StringBuilder(); 144 | } else { 145 | sb.append(':'); 146 | } 147 | sb.append(cn.flattenToString()); 148 | } 149 | Settings.Secure.putString(mContentResolver, mConfig.setting, 150 | sb != null ? sb.toString() : ""); 151 | } 152 | 153 | private void loadEnabledServices() { 154 | mEnabledServices.clear(); 155 | final String flat = Settings.Secure.getString(mContentResolver, mConfig.setting); 156 | if (flat != null && !"".equals(flat)) { 157 | final String[] names = flat.split(":"); 158 | for (int i = 0; i < names.length; i++) { 159 | final ComponentName cn = ComponentName.unflattenFromString(names[i]); 160 | if (cn != null) { 161 | mEnabledServices.add(cn); 162 | } 163 | } 164 | } 165 | } 166 | 167 | public List reload() { 168 | loadEnabledServices(); 169 | getServices(mConfig, mServices, mContext.getPackageManager()); 170 | for (Callback callback : mCallbacks) { 171 | callback.onServicesReloaded(mServices); 172 | } 173 | return mServices; 174 | } 175 | 176 | public boolean isEnabled(ComponentName cn) { 177 | return mEnabledServices.contains(cn); 178 | } 179 | 180 | public void setEnabled(ComponentName cn, boolean enabled) { 181 | if (enabled) { 182 | mEnabledServices.add(cn); 183 | } else { 184 | mEnabledServices.remove(cn); 185 | } 186 | saveEnabledServices(); 187 | } 188 | 189 | private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 190 | @Override 191 | public void onChange(boolean selfChange, Uri uri) { 192 | reload(); 193 | } 194 | }; 195 | 196 | private final BroadcastReceiver mPackageReceiver = new BroadcastReceiver() { 197 | @Override 198 | public void onReceive(Context context, Intent intent) { 199 | reload(); 200 | } 201 | }; 202 | 203 | public interface Callback { 204 | void onServicesReloaded(List services); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /notificationlight/BatteryLightSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The CyanogenMod Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notificationlight; 18 | 19 | import android.content.ContentResolver; 20 | import android.content.res.Resources; 21 | import android.os.Bundle; 22 | import android.preference.Preference; 23 | import android.preference.PreferenceGroup; 24 | import android.preference.PreferenceScreen; 25 | import android.provider.Settings; 26 | import android.view.Menu; 27 | import android.view.MenuInflater; 28 | import android.view.MenuItem; 29 | 30 | import com.android.settings.R; 31 | import com.android.settings.SettingsPreferenceFragment; 32 | import com.android.settings.cyanogenmod.CMSystemSettingSwitchPreference; 33 | 34 | import cyanogenmod.providers.CMSettings; 35 | 36 | import org.cyanogenmod.internal.logging.CMMetricsLogger; 37 | 38 | public class BatteryLightSettings extends SettingsPreferenceFragment implements 39 | Preference.OnPreferenceChangeListener { 40 | private static final String TAG = "BatteryLightSettings"; 41 | 42 | private static final String LOW_COLOR_PREF = "low_color"; 43 | private static final String MEDIUM_COLOR_PREF = "medium_color"; 44 | private static final String FULL_COLOR_PREF = "full_color"; 45 | private static final String LIGHT_ENABLED_PREF = "battery_light_enabled"; 46 | private static final String PULSE_ENABLED_PREF = "battery_light_pulse"; 47 | 48 | private PreferenceGroup mColorPrefs; 49 | private ApplicationLightPreference mLowColorPref; 50 | private ApplicationLightPreference mMediumColorPref; 51 | private ApplicationLightPreference mFullColorPref; 52 | private CMSystemSettingSwitchPreference mLightEnabledPref; 53 | private CMSystemSettingSwitchPreference mPulseEnabledPref; 54 | 55 | private static final int MENU_RESET = Menu.FIRST; 56 | 57 | @Override 58 | protected int getMetricsCategory() { 59 | return CMMetricsLogger.BATTERY_LIGHT_SETTINGS; 60 | } 61 | 62 | @Override 63 | public void onCreate(Bundle savedInstanceState) { 64 | super.onCreate(savedInstanceState); 65 | addPreferencesFromResource(R.xml.battery_light_settings); 66 | 67 | PreferenceScreen prefSet = getPreferenceScreen(); 68 | 69 | PreferenceGroup mGeneralPrefs = (PreferenceGroup) prefSet.findPreference("general_section"); 70 | 71 | mLightEnabledPref = (CMSystemSettingSwitchPreference) prefSet.findPreference(LIGHT_ENABLED_PREF); 72 | mPulseEnabledPref = (CMSystemSettingSwitchPreference) prefSet.findPreference(PULSE_ENABLED_PREF); 73 | 74 | if (!getResources().getBoolean(com.android.internal.R.bool.config_ledCanPulse) || 75 | getResources().getBoolean(org.cyanogenmod.platform.internal.R.bool.config_useSegmentedBatteryLed)) { 76 | mGeneralPrefs.removePreference(mPulseEnabledPref); 77 | } 78 | 79 | // Does the Device support changing battery LED colors? 80 | if (getResources().getBoolean(com.android.internal.R.bool.config_multiColorBatteryLed)) { 81 | setHasOptionsMenu(true); 82 | 83 | // Low, Medium and full color preferences 84 | mLowColorPref = (ApplicationLightPreference) prefSet.findPreference(LOW_COLOR_PREF); 85 | mLowColorPref.setOnPreferenceChangeListener(this); 86 | 87 | mMediumColorPref = (ApplicationLightPreference) prefSet.findPreference(MEDIUM_COLOR_PREF); 88 | mMediumColorPref.setOnPreferenceChangeListener(this); 89 | 90 | mFullColorPref = (ApplicationLightPreference) prefSet.findPreference(FULL_COLOR_PREF); 91 | mFullColorPref.setOnPreferenceChangeListener(this); 92 | } else { 93 | prefSet.removePreference(prefSet.findPreference("colors_list")); 94 | resetColors(); 95 | } 96 | } 97 | 98 | @Override 99 | public void onResume() { 100 | super.onResume(); 101 | refreshDefault(); 102 | } 103 | 104 | private void refreshDefault() { 105 | ContentResolver resolver = getContentResolver(); 106 | Resources res = getResources(); 107 | 108 | if (mLowColorPref != null) { 109 | int lowColor = CMSettings.System.getInt(resolver, CMSettings.System.BATTERY_LIGHT_LOW_COLOR, 110 | res.getInteger(com.android.internal.R.integer.config_notificationsBatteryLowARGB)); 111 | mLowColorPref.setAllValues(lowColor, 0, 0, false); 112 | } 113 | 114 | if (mMediumColorPref != null) { 115 | int mediumColor = CMSettings.System.getInt(resolver, CMSettings.System.BATTERY_LIGHT_MEDIUM_COLOR, 116 | res.getInteger(com.android.internal.R.integer.config_notificationsBatteryMediumARGB)); 117 | mMediumColorPref.setAllValues(mediumColor, 0, 0, false); 118 | } 119 | 120 | if (mFullColorPref != null) { 121 | int fullColor = CMSettings.System.getInt(resolver, CMSettings.System.BATTERY_LIGHT_FULL_COLOR, 122 | res.getInteger(com.android.internal.R.integer.config_notificationsBatteryFullARGB)); 123 | mFullColorPref.setAllValues(fullColor, 0, 0, false); 124 | } 125 | } 126 | 127 | /** 128 | * Updates the default or application specific notification settings. 129 | * 130 | * @param key of the specific setting to update 131 | * @param color 132 | */ 133 | protected void updateValues(String key, Integer color) { 134 | ContentResolver resolver = getContentResolver(); 135 | 136 | if (key.equals(LOW_COLOR_PREF)) { 137 | CMSettings.System.putInt(resolver, CMSettings.System.BATTERY_LIGHT_LOW_COLOR, color); 138 | } else if (key.equals(MEDIUM_COLOR_PREF)) { 139 | CMSettings.System.putInt(resolver, CMSettings.System.BATTERY_LIGHT_MEDIUM_COLOR, color); 140 | } else if (key.equals(FULL_COLOR_PREF)) { 141 | CMSettings.System.putInt(resolver, CMSettings.System.BATTERY_LIGHT_FULL_COLOR, color); 142 | } 143 | } 144 | 145 | @Override 146 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 147 | menu.add(0, MENU_RESET, 0, R.string.profile_reset_title) 148 | .setIcon(R.drawable.ic_settings_backup_restore) 149 | .setAlphabeticShortcut('r') 150 | .setShowAsActionFlags(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); 151 | } 152 | 153 | @Override 154 | public boolean onOptionsItemSelected(MenuItem item) { 155 | switch (item.getItemId()) { 156 | case MENU_RESET: 157 | resetToDefaults(); 158 | return true; 159 | } 160 | return false; 161 | } 162 | 163 | protected void resetColors() { 164 | ContentResolver resolver = getContentResolver(); 165 | Resources res = getResources(); 166 | 167 | // Reset to the framework default colors 168 | CMSettings.System.putInt(resolver, CMSettings.System.BATTERY_LIGHT_LOW_COLOR, 169 | res.getInteger(com.android.internal.R.integer.config_notificationsBatteryLowARGB)); 170 | CMSettings.System.putInt(resolver, CMSettings.System.BATTERY_LIGHT_MEDIUM_COLOR, 171 | res.getInteger(com.android.internal.R.integer.config_notificationsBatteryMediumARGB)); 172 | CMSettings.System.putInt(resolver, CMSettings.System.BATTERY_LIGHT_FULL_COLOR, 173 | res.getInteger(com.android.internal.R.integer.config_notificationsBatteryFullARGB)); 174 | refreshDefault(); 175 | } 176 | 177 | protected void resetToDefaults() { 178 | final Resources res = getResources(); 179 | final boolean batteryLightEnabled = res.getBoolean(R.bool.def_battery_light_enabled); 180 | final boolean batteryLightPulseEnabled = res.getBoolean(R.bool.def_battery_light_pulse); 181 | 182 | if (mLightEnabledPref != null) mLightEnabledPref.setChecked(batteryLightEnabled); 183 | if (mPulseEnabledPref != null) mPulseEnabledPref.setChecked(batteryLightPulseEnabled); 184 | 185 | resetColors(); 186 | } 187 | 188 | @Override 189 | public boolean onPreferenceChange(Preference preference, Object objValue) { 190 | ApplicationLightPreference lightPref = (ApplicationLightPreference) preference; 191 | updateValues(lightPref.getKey(), lightPref.getColor()); 192 | 193 | return true; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /notification/ZenAccessSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.app.AlertDialog; 20 | import android.app.Dialog; 21 | import android.app.DialogFragment; 22 | import android.app.NotificationManager; 23 | import android.content.Context; 24 | import android.content.DialogInterface; 25 | import android.content.pm.ApplicationInfo; 26 | import android.content.pm.PackageItemInfo; 27 | import android.content.pm.PackageManager; 28 | import android.database.ContentObserver; 29 | import android.net.Uri; 30 | import android.os.AsyncTask; 31 | import android.os.Bundle; 32 | import android.os.Handler; 33 | import android.os.Looper; 34 | import android.preference.Preference; 35 | import android.preference.Preference.OnPreferenceChangeListener; 36 | import android.preference.PreferenceScreen; 37 | import android.preference.SwitchPreference; 38 | import android.provider.Settings.Secure; 39 | import android.text.TextUtils; 40 | import android.util.ArraySet; 41 | import android.view.LayoutInflater; 42 | import android.view.View; 43 | import android.view.ViewGroup; 44 | import android.widget.TextView; 45 | 46 | import com.android.internal.logging.MetricsLogger; 47 | import com.android.settings.R; 48 | import com.android.settings.SettingsPreferenceFragment; 49 | 50 | import java.util.ArrayList; 51 | import java.util.Collections; 52 | import java.util.List; 53 | 54 | public class ZenAccessSettings extends SettingsPreferenceFragment { 55 | 56 | private final SettingObserver mObserver = new SettingObserver(); 57 | 58 | private Context mContext; 59 | private PackageManager mPkgMan; 60 | private NotificationManager mNoMan; 61 | private TextView mEmpty; 62 | 63 | @Override 64 | protected int getMetricsCategory() { 65 | return MetricsLogger.NOTIFICATION_ZEN_MODE_ACCESS; 66 | } 67 | 68 | @Override 69 | public void onCreate(Bundle icicle) { 70 | super.onCreate(icicle); 71 | 72 | mContext = getActivity(); 73 | mPkgMan = mContext.getPackageManager(); 74 | mNoMan = mContext.getSystemService(NotificationManager.class); 75 | setPreferenceScreen(getPreferenceManager().createPreferenceScreen(mContext)); 76 | } 77 | 78 | @Override 79 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 80 | Bundle savedInstanceState) { 81 | final View v = inflater.inflate(R.layout.managed_service_settings, container, false); 82 | mEmpty = (TextView) v.findViewById(android.R.id.empty); 83 | mEmpty.setText(R.string.zen_access_empty_text); 84 | return v; 85 | } 86 | 87 | @Override 88 | public void onResume() { 89 | super.onResume(); 90 | reloadList(); 91 | getContentResolver().registerContentObserver( 92 | Secure.getUriFor(Secure.ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES), false, 93 | mObserver); 94 | } 95 | 96 | @Override 97 | public void onPause() { 98 | super.onPause(); 99 | getContentResolver().unregisterContentObserver(mObserver); 100 | } 101 | 102 | private void reloadList() { 103 | final PreferenceScreen screen = getPreferenceScreen(); 104 | screen.removeAll(); 105 | final ArrayList apps = new ArrayList<>(); 106 | final ArraySet requesting = mNoMan.getPackagesRequestingNotificationPolicyAccess(); 107 | if (requesting != null && !requesting.isEmpty()) { 108 | final List installed = mPkgMan.getInstalledApplications(0); 109 | if (installed != null) { 110 | for (ApplicationInfo app : installed) { 111 | if (requesting.contains(app.packageName)) { 112 | apps.add(app); 113 | } 114 | } 115 | } 116 | } 117 | Collections.sort(apps, new PackageItemInfo.DisplayNameComparator(mPkgMan)); 118 | for (ApplicationInfo app : apps) { 119 | final String pkg = app.packageName; 120 | final CharSequence label = app.loadLabel(mPkgMan); 121 | final SwitchPreference pref = new SwitchPreference(mContext); 122 | pref.setPersistent(false); 123 | pref.setIcon(app.loadIcon(mPkgMan)); 124 | pref.setTitle(label); 125 | pref.setChecked(hasAccess(pkg)); 126 | pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 127 | @Override 128 | public boolean onPreferenceChange(Preference preference, Object newValue) { 129 | final boolean access = (Boolean) newValue; 130 | if (!access) { 131 | // disabling access 132 | setAccess(mContext, pkg, access); 133 | return true; 134 | } 135 | // enabling access: show a scary dialog first 136 | new ScaryWarningDialogFragment() 137 | .setPkgInfo(pkg, label) 138 | .show(getFragmentManager(), "dialog"); 139 | return false; 140 | } 141 | }); 142 | screen.addPreference(pref); 143 | } 144 | mEmpty.setVisibility(apps.isEmpty() ? View.VISIBLE : View.GONE); 145 | } 146 | 147 | private boolean hasAccess(String pkg) { 148 | return mNoMan.isNotificationPolicyAccessGrantedForPackage(pkg); 149 | } 150 | 151 | private static void setAccess(final Context context, final String pkg, final boolean access) { 152 | AsyncTask.execute(new Runnable() { 153 | @Override 154 | public void run() { 155 | final NotificationManager mgr = context.getSystemService(NotificationManager.class); 156 | mgr.setNotificationPolicyAccessGranted(pkg, access); 157 | } 158 | }); 159 | } 160 | 161 | private final class SettingObserver extends ContentObserver { 162 | public SettingObserver() { 163 | super(new Handler(Looper.getMainLooper())); 164 | } 165 | 166 | @Override 167 | public void onChange(boolean selfChange, Uri uri) { 168 | reloadList(); 169 | } 170 | } 171 | 172 | public static class ScaryWarningDialogFragment extends DialogFragment { 173 | static final String KEY_PKG = "p"; 174 | static final String KEY_LABEL = "l"; 175 | 176 | public ScaryWarningDialogFragment setPkgInfo(String pkg, CharSequence label) { 177 | Bundle args = new Bundle(); 178 | args.putString(KEY_PKG, pkg); 179 | args.putString(KEY_LABEL, TextUtils.isEmpty(label) ? pkg : label.toString()); 180 | setArguments(args); 181 | return this; 182 | } 183 | 184 | @Override 185 | public Dialog onCreateDialog(Bundle savedInstanceState) { 186 | super.onCreate(savedInstanceState); 187 | final Bundle args = getArguments(); 188 | final String pkg = args.getString(KEY_PKG); 189 | final String label = args.getString(KEY_LABEL); 190 | 191 | final String title = getResources().getString(R.string.zen_access_warning_dialog_title, 192 | label); 193 | final String summary = getResources() 194 | .getString(R.string.zen_access_warning_dialog_summary); 195 | return new AlertDialog.Builder(getContext()) 196 | .setMessage(summary) 197 | .setTitle(title) 198 | .setCancelable(true) 199 | .setPositiveButton(R.string.allow, 200 | new DialogInterface.OnClickListener() { 201 | public void onClick(DialogInterface dialog, int id) { 202 | setAccess(getContext(), pkg, true); 203 | } 204 | }) 205 | .setNegativeButton(R.string.deny, 206 | new DialogInterface.OnClickListener() { 207 | public void onClick(DialogInterface dialog, int id) { 208 | // pass 209 | } 210 | }) 211 | .create(); 212 | } 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /notification/ZenModeEventRuleSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.content.Context; 20 | import android.content.pm.PackageManager.NameNotFoundException; 21 | import android.database.Cursor; 22 | import android.os.UserHandle; 23 | import android.os.UserManager; 24 | import android.preference.PreferenceScreen; 25 | import android.provider.CalendarContract.Calendars; 26 | import android.provider.Settings; 27 | import android.service.notification.ZenModeConfig; 28 | import android.service.notification.ZenModeConfig.EventInfo; 29 | import android.service.notification.ZenModeConfig.ZenRule; 30 | 31 | import com.android.internal.logging.MetricsLogger; 32 | import com.android.settings.DropDownPreference; 33 | import com.android.settings.R; 34 | 35 | import java.util.ArrayList; 36 | import java.util.Collections; 37 | import java.util.Comparator; 38 | import java.util.List; 39 | 40 | public class ZenModeEventRuleSettings extends ZenModeRuleSettingsBase { 41 | private static final String KEY_CALENDAR = "calendar"; 42 | private static final String KEY_REPLY = "reply"; 43 | 44 | public static final String ACTION = Settings.ACTION_ZEN_MODE_EVENT_RULE_SETTINGS; 45 | 46 | private DropDownPreference mCalendar; 47 | private DropDownPreference mReply; 48 | 49 | private EventInfo mEvent; 50 | private List mCalendars; 51 | private boolean mCreate; 52 | 53 | @Override 54 | protected boolean setRule(ZenRule rule) { 55 | mEvent = rule != null ? ZenModeConfig.tryParseEventConditionId(rule.conditionId) 56 | : null; 57 | return mEvent != null; 58 | } 59 | 60 | @Override 61 | protected String getZenModeDependency() { 62 | return null; 63 | } 64 | 65 | @Override 66 | protected int getEnabledToastText() { 67 | return R.string.zen_event_rule_enabled_toast; 68 | } 69 | 70 | @Override 71 | public void onResume() { 72 | super.onResume(); 73 | if (!mCreate) { 74 | reloadCalendar(); 75 | } 76 | mCreate = false; 77 | } 78 | 79 | private void reloadCalendar() { 80 | mCalendars = getCalendars(mContext); 81 | mCalendar.clearItems(); 82 | mCalendar.addItem(R.string.zen_mode_event_rule_calendar_any, key(0, null)); 83 | final String eventCalendar = mEvent != null ? mEvent.calendar : null; 84 | boolean found = false; 85 | for (CalendarInfo calendar : mCalendars) { 86 | mCalendar.addItem(calendar.name, key(calendar)); 87 | if (eventCalendar != null && eventCalendar.equals(calendar.name)) { 88 | found = true; 89 | } 90 | } 91 | if (eventCalendar != null && !found) { 92 | mCalendar.addItem(eventCalendar, key(mEvent.userId, eventCalendar)); 93 | } 94 | } 95 | 96 | @Override 97 | protected void onCreateInternal() { 98 | mCreate = true; 99 | addPreferencesFromResource(R.xml.zen_mode_event_rule_settings); 100 | final PreferenceScreen root = getPreferenceScreen(); 101 | 102 | mCalendar = (DropDownPreference) root.findPreference(KEY_CALENDAR); 103 | mCalendar.setCallback(new DropDownPreference.Callback() { 104 | @Override 105 | public boolean onItemSelected(int pos, Object value) { 106 | final String calendarKey = (String) value; 107 | if (calendarKey.equals(key(mEvent))) return true; 108 | final int i = calendarKey.indexOf(':'); 109 | mEvent.userId = Integer.parseInt(calendarKey.substring(0, i)); 110 | mEvent.calendar = calendarKey.substring(i + 1); 111 | if (mEvent.calendar.isEmpty()) { 112 | mEvent.calendar = null; 113 | } 114 | updateRule(ZenModeConfig.toEventConditionId(mEvent)); 115 | return true; 116 | } 117 | }); 118 | 119 | mReply = (DropDownPreference) root.findPreference(KEY_REPLY); 120 | mReply.addItem(R.string.zen_mode_event_rule_reply_any_except_no, 121 | EventInfo.REPLY_ANY_EXCEPT_NO); 122 | mReply.addItem(R.string.zen_mode_event_rule_reply_yes_or_maybe, 123 | EventInfo.REPLY_YES_OR_MAYBE); 124 | mReply.addItem(R.string.zen_mode_event_rule_reply_yes, 125 | EventInfo.REPLY_YES); 126 | mReply.setCallback(new DropDownPreference.Callback() { 127 | @Override 128 | public boolean onItemSelected(int pos, Object value) { 129 | final int reply = (Integer) value; 130 | if (reply == mEvent.reply) return true; 131 | mEvent.reply = reply; 132 | updateRule(ZenModeConfig.toEventConditionId(mEvent)); 133 | return true; 134 | } 135 | }); 136 | 137 | reloadCalendar(); 138 | updateControlsInternal(); 139 | } 140 | 141 | @Override 142 | protected void updateControlsInternal() { 143 | mCalendar.setSelectedValue(key(mEvent)); 144 | mReply.setSelectedValue(mEvent.reply); 145 | } 146 | 147 | @Override 148 | protected int getMetricsCategory() { 149 | return MetricsLogger.NOTIFICATION_ZEN_MODE_EVENT_RULE; 150 | } 151 | 152 | public static CalendarInfo findCalendar(Context context, EventInfo event) { 153 | if (context == null || event == null) return null; 154 | final String eventKey = key(event); 155 | for (CalendarInfo calendar : getCalendars(context)) { 156 | if (eventKey.equals(key(calendar))) { 157 | return calendar; 158 | } 159 | } 160 | return null; 161 | } 162 | 163 | private static List getCalendars(Context context) { 164 | final List calendars = new ArrayList<>(); 165 | for (UserHandle user : UserManager.get(context).getUserProfiles()) { 166 | final Context userContext = getContextForUser(context, user); 167 | if (userContext != null) { 168 | addCalendars(userContext, calendars); 169 | } 170 | } 171 | Collections.sort(calendars, CALENDAR_NAME); 172 | return calendars; 173 | } 174 | 175 | private static Context getContextForUser(Context context, UserHandle user) { 176 | try { 177 | return context.createPackageContextAsUser(context.getPackageName(), 0, user); 178 | } catch (NameNotFoundException e) { 179 | return null; 180 | } 181 | } 182 | 183 | public static void addCalendars(Context context, List outCalendars) { 184 | final String primary = "\"primary\""; 185 | final String[] projection = { Calendars._ID, Calendars.CALENDAR_DISPLAY_NAME, 186 | "(" + Calendars.ACCOUNT_NAME + "=" + Calendars.OWNER_ACCOUNT + ") AS " + primary }; 187 | final String selection = primary + " = 1"; 188 | Cursor cursor = null; 189 | try { 190 | cursor = context.getContentResolver().query(Calendars.CONTENT_URI, projection, 191 | selection, null, null); 192 | if (cursor == null) { 193 | return; 194 | } 195 | while (cursor.moveToNext()) { 196 | final CalendarInfo ci = new CalendarInfo(); 197 | ci.name = cursor.getString(1); 198 | ci.userId = context.getUserId(); 199 | outCalendars.add(ci); 200 | } 201 | } finally { 202 | if (cursor != null) { 203 | cursor.close(); 204 | } 205 | } 206 | } 207 | 208 | private static String key(CalendarInfo calendar) { 209 | return key(calendar.userId, calendar.name); 210 | } 211 | 212 | private static String key(EventInfo event) { 213 | return key(event.userId, event.calendar); 214 | } 215 | 216 | private static String key(int userId, String calendar) { 217 | return EventInfo.resolveUserId(userId) + ":" + (calendar == null ? "" : calendar); 218 | } 219 | 220 | private static final Comparator CALENDAR_NAME = new Comparator() { 221 | @Override 222 | public int compare(CalendarInfo lhs, CalendarInfo rhs) { 223 | return lhs.name.compareTo(rhs.name); 224 | } 225 | }; 226 | 227 | public static class CalendarInfo { 228 | public String name; 229 | public int userId; 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /notification/IncreasingRingVolumePreference.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 CyanogenMod Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.content.ContentResolver; 20 | import android.content.Context; 21 | import android.media.AudioAttributes; 22 | import android.media.AudioManager; 23 | import android.media.Ringtone; 24 | import android.media.RingtoneManager; 25 | import android.os.Handler; 26 | import android.os.HandlerThread; 27 | import android.os.Message; 28 | import android.preference.PreferenceManager; 29 | import android.preference.Preference; 30 | import android.provider.Settings; 31 | import android.text.format.Formatter; 32 | import android.util.AttributeSet; 33 | import android.util.Log; 34 | import android.view.View; 35 | import android.widget.SeekBar; 36 | import android.widget.TextView; 37 | 38 | import com.android.settings.R; 39 | import cyanogenmod.providers.CMSettings; 40 | 41 | public class IncreasingRingVolumePreference extends Preference implements 42 | PreferenceManager.OnActivityStopListener, Handler.Callback, 43 | SeekBar.OnSeekBarChangeListener { 44 | private static final String TAG = "IncreasingRingMinVolumePreference"; 45 | 46 | public interface Callback { 47 | void onStartingSample(); 48 | } 49 | 50 | private SeekBar mStartVolumeSeekBar; 51 | private SeekBar mRampUpTimeSeekBar; 52 | private TextView mRampUpTimeValue; 53 | 54 | private Ringtone mRingtone; 55 | private Callback mCallback; 56 | 57 | private Handler mHandler; 58 | private final Handler mMainHandler = new Handler(this); 59 | 60 | private static final int MSG_START_SAMPLE = 1; 61 | private static final int MSG_STOP_SAMPLE = 2; 62 | private static final int MSG_INIT_SAMPLE = 3; 63 | private static final int MSG_SET_VOLUME = 4; 64 | private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000; 65 | 66 | public IncreasingRingVolumePreference(Context context) { 67 | this(context, null); 68 | } 69 | 70 | public IncreasingRingVolumePreference(Context context, AttributeSet attrs) { 71 | this(context, attrs, 0); 72 | } 73 | 74 | public IncreasingRingVolumePreference(Context context, AttributeSet attrs, 75 | int defStyleAttr) { 76 | this(context, attrs, defStyleAttr, 0); 77 | } 78 | 79 | public IncreasingRingVolumePreference(Context context, AttributeSet attrs, 80 | int defStyleAttr, int defStyleRes) { 81 | super(context, attrs, defStyleAttr, defStyleRes); 82 | setLayoutResource(R.layout.preference_increasing_ring); 83 | initHandler(); 84 | } 85 | 86 | public void setCallback(Callback callback) { 87 | mCallback = callback; 88 | } 89 | 90 | public void onActivityResume() { 91 | initHandler(); 92 | } 93 | 94 | @Override 95 | public void onActivityStop() { 96 | if (mHandler != null) { 97 | postStopSample(); 98 | mHandler.getLooper().quitSafely(); 99 | mHandler = null; 100 | } 101 | } 102 | 103 | @Override 104 | public boolean handleMessage(Message msg) { 105 | switch (msg.what) { 106 | case MSG_START_SAMPLE: 107 | onStartSample((float) msg.arg1 / 1000F); 108 | break; 109 | case MSG_STOP_SAMPLE: 110 | onStopSample(); 111 | break; 112 | case MSG_INIT_SAMPLE: 113 | onInitSample(); 114 | break; 115 | case MSG_SET_VOLUME: 116 | onSetVolume((float) msg.arg1 / 1000F); 117 | break; 118 | } 119 | return true; 120 | } 121 | 122 | @Override 123 | protected void onBindView(View view) { 124 | super.onBindView(view); 125 | getPreferenceManager().registerOnActivityStopListener(this); 126 | 127 | initHandler(); 128 | 129 | final SeekBar seekBar = (SeekBar) view.findViewById(R.id.start_volume); 130 | if (seekBar == mStartVolumeSeekBar) return; 131 | 132 | mStartVolumeSeekBar = seekBar; 133 | mRampUpTimeSeekBar = (SeekBar) view.findViewById(R.id.ramp_up_time); 134 | mRampUpTimeValue = (TextView) view.findViewById(R.id.ramp_up_time_value); 135 | 136 | final ContentResolver cr = getContext().getContentResolver(); 137 | float startVolume = CMSettings.System.getFloat(cr, 138 | CMSettings.System.INCREASING_RING_START_VOLUME, 0.1f); 139 | int rampUpTime = CMSettings.System.getInt(cr, 140 | CMSettings.System.INCREASING_RING_RAMP_UP_TIME, 10); 141 | 142 | mStartVolumeSeekBar.setProgress(Math.round(startVolume * 1000F)); 143 | mStartVolumeSeekBar.setOnSeekBarChangeListener(this); 144 | mRampUpTimeSeekBar.setOnSeekBarChangeListener(this); 145 | mRampUpTimeSeekBar.setProgress((rampUpTime / 5) - 1); 146 | mRampUpTimeValue.setText( 147 | Formatter.formatShortElapsedTime(getContext(), rampUpTime * 1000)); 148 | } 149 | 150 | @Override 151 | public void onStartTrackingTouch(SeekBar seekBar) { 152 | } 153 | 154 | @Override 155 | public void onStopTrackingTouch(SeekBar seekBar) { 156 | if (seekBar == mStartVolumeSeekBar) { 157 | postStartSample(seekBar.getProgress()); 158 | } 159 | } 160 | 161 | @Override 162 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) { 163 | ContentResolver cr = getContext().getContentResolver(); 164 | if (fromTouch && seekBar == mStartVolumeSeekBar) { 165 | CMSettings.System.putFloat(cr, CMSettings.System.INCREASING_RING_START_VOLUME, 166 | (float) progress / 1000F); 167 | } else if (seekBar == mRampUpTimeSeekBar) { 168 | int seconds = (progress + 1) * 5; 169 | mRampUpTimeValue.setText( 170 | Formatter.formatShortElapsedTime(getContext(), seconds * 1000)); 171 | if (fromTouch) { 172 | CMSettings.System.putInt(cr, 173 | CMSettings.System.INCREASING_RING_RAMP_UP_TIME, seconds); 174 | } 175 | } 176 | } 177 | 178 | private void initHandler() { 179 | if (mHandler != null) return; 180 | 181 | HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler"); 182 | thread.start(); 183 | 184 | mHandler = new Handler(thread.getLooper(), this); 185 | mHandler.sendEmptyMessage(MSG_INIT_SAMPLE); 186 | } 187 | 188 | private void onInitSample() { 189 | mRingtone = RingtoneManager.getRingtone(getContext(), 190 | Settings.System.DEFAULT_RINGTONE_URI); 191 | if (mRingtone != null) { 192 | mRingtone.setStreamType(AudioManager.STREAM_RING); 193 | mRingtone.setAudioAttributes( 194 | new AudioAttributes.Builder(mRingtone.getAudioAttributes()) 195 | .setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY | 196 | AudioAttributes.FLAG_BYPASS_MUTE) 197 | .build()); 198 | } 199 | } 200 | 201 | private void postStartSample(int progress) { 202 | boolean playing = isSamplePlaying(); 203 | mHandler.removeMessages(MSG_START_SAMPLE); 204 | mHandler.removeMessages(MSG_SET_VOLUME); 205 | mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE, progress, 0), 206 | playing ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0); 207 | if (playing) { 208 | mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_VOLUME, progress, 0)); 209 | } 210 | } 211 | 212 | private void onStartSample(float volume) { 213 | if (mRingtone == null) { 214 | return; 215 | } 216 | if (!isSamplePlaying()) { 217 | if (mCallback != null) { 218 | mCallback.onStartingSample(); 219 | } 220 | try { 221 | mRingtone.play(); 222 | } catch (Throwable e) { 223 | Log.w(TAG, "Error playing ringtone", e); 224 | } 225 | } 226 | mRingtone.setVolume(volume); 227 | } 228 | 229 | private void onSetVolume(float volume) { 230 | if (mRingtone != null) { 231 | mRingtone.setVolume(volume); 232 | } 233 | } 234 | 235 | private boolean isSamplePlaying() { 236 | return mRingtone != null && mRingtone.isPlaying(); 237 | } 238 | 239 | public void stopSample() { 240 | if (mHandler != null) { 241 | postStopSample(); 242 | } 243 | } 244 | 245 | private void postStopSample() { 246 | // remove pending delayed start messages 247 | mHandler.removeMessages(MSG_START_SAMPLE); 248 | mHandler.removeMessages(MSG_STOP_SAMPLE); 249 | mHandler.sendEmptyMessage(MSG_STOP_SAMPLE); 250 | } 251 | 252 | private void onStopSample() { 253 | if (mRingtone != null) { 254 | mRingtone.stop(); 255 | } 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /notification/ZenModePrioritySettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.os.Bundle; 20 | import android.preference.Preference; 21 | import android.preference.Preference.OnPreferenceChangeListener; 22 | import android.preference.PreferenceScreen; 23 | import android.preference.SwitchPreference; 24 | import android.service.notification.ZenModeConfig; 25 | import android.util.Log; 26 | 27 | import com.android.internal.logging.MetricsLogger; 28 | import com.android.settings.DropDownPreference; 29 | import com.android.settings.R; 30 | import com.android.settings.search.Indexable; 31 | 32 | public class ZenModePrioritySettings extends ZenModeSettingsBase implements Indexable { 33 | private static final String KEY_REMINDERS = "reminders"; 34 | private static final String KEY_EVENTS = "events"; 35 | private static final String KEY_MESSAGES = "messages"; 36 | private static final String KEY_CALLS = "calls"; 37 | private static final String KEY_REPEAT_CALLERS = "repeat_callers"; 38 | private static final String KEY_ALLOW_LIGHTS = "zen_priority_allow_lights"; 39 | 40 | private static final int SOURCE_NONE = -1; 41 | 42 | private boolean mDisableListeners; 43 | private SwitchPreference mReminders; 44 | private SwitchPreference mEvents; 45 | private DropDownPreference mMessages; 46 | private DropDownPreference mCalls; 47 | private SwitchPreference mRepeatCallers; 48 | 49 | @Override 50 | public void onCreate(Bundle savedInstanceState) { 51 | super.onCreate(savedInstanceState); 52 | addPreferencesFromResource(R.xml.zen_mode_priority_settings); 53 | final PreferenceScreen root = getPreferenceScreen(); 54 | 55 | mReminders = (SwitchPreference) root.findPreference(KEY_REMINDERS); 56 | mReminders.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 57 | @Override 58 | public boolean onPreferenceChange(Preference preference, Object newValue) { 59 | if (mDisableListeners) return true; 60 | final boolean val = (Boolean) newValue; 61 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ALLOW_REMINDERS, val); 62 | if (val == mConfig.allowReminders) return true; 63 | if (DEBUG) Log.d(TAG, "onPrefChange allowReminders=" + val); 64 | final ZenModeConfig newConfig = mConfig.copy(); 65 | newConfig.allowReminders = val; 66 | return setZenModeConfig(newConfig); 67 | } 68 | }); 69 | 70 | mEvents = (SwitchPreference) root.findPreference(KEY_EVENTS); 71 | mEvents.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 72 | @Override 73 | public boolean onPreferenceChange(Preference preference, Object newValue) { 74 | if (mDisableListeners) return true; 75 | final boolean val = (Boolean) newValue; 76 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ALLOW_EVENTS, val); 77 | if (val == mConfig.allowEvents) return true; 78 | if (DEBUG) Log.d(TAG, "onPrefChange allowEvents=" + val); 79 | final ZenModeConfig newConfig = mConfig.copy(); 80 | newConfig.allowEvents = val; 81 | return setZenModeConfig(newConfig); 82 | } 83 | }); 84 | 85 | mMessages = (DropDownPreference) root.findPreference(KEY_MESSAGES); 86 | addSources(mMessages); 87 | mMessages.setCallback(new DropDownPreference.Callback() { 88 | @Override 89 | public boolean onItemSelected(int pos, Object newValue) { 90 | if (mDisableListeners) return true; 91 | final int val = (Integer) newValue; 92 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ALLOW_MESSAGES, val); 93 | final boolean allowMessages = val != SOURCE_NONE; 94 | final int allowMessagesFrom = val == SOURCE_NONE ? mConfig.allowMessagesFrom : val; 95 | if (allowMessages == mConfig.allowMessages 96 | && allowMessagesFrom == mConfig.allowMessagesFrom) { 97 | return true; 98 | } 99 | if (DEBUG) Log.d(TAG, "onPrefChange allowMessages=" + allowMessages 100 | + " allowMessagesFrom=" + ZenModeConfig.sourceToString(allowMessagesFrom)); 101 | final ZenModeConfig newConfig = mConfig.copy(); 102 | newConfig.allowMessages = allowMessages; 103 | newConfig.allowMessagesFrom = allowMessagesFrom; 104 | return setZenModeConfig(newConfig); 105 | } 106 | }); 107 | 108 | mCalls = (DropDownPreference) root.findPreference(KEY_CALLS); 109 | addSources(mCalls); 110 | mCalls.setCallback(new DropDownPreference.Callback() { 111 | @Override 112 | public boolean onItemSelected(int pos, Object newValue) { 113 | if (mDisableListeners) return true; 114 | final int val = (Integer) newValue; 115 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ALLOW_CALLS, val); 116 | final boolean allowCalls = val != SOURCE_NONE; 117 | final int allowCallsFrom = val == SOURCE_NONE ? mConfig.allowCallsFrom : val; 118 | if (allowCalls == mConfig.allowCalls 119 | && allowCallsFrom == mConfig.allowCallsFrom) { 120 | return true; 121 | } 122 | if (DEBUG) Log.d(TAG, "onPrefChange allowCalls=" + allowCalls 123 | + " allowCallsFrom=" + ZenModeConfig.sourceToString(allowCallsFrom)); 124 | final ZenModeConfig newConfig = mConfig.copy(); 125 | newConfig.allowCalls = allowCalls; 126 | newConfig.allowCallsFrom = allowCallsFrom; 127 | return setZenModeConfig(newConfig); 128 | } 129 | }); 130 | 131 | mRepeatCallers = (SwitchPreference) root.findPreference(KEY_REPEAT_CALLERS); 132 | mRepeatCallers.setSummary(mContext.getString(R.string.zen_mode_repeat_callers_summary, 133 | mContext.getResources().getInteger(com.android.internal.R.integer 134 | .config_zen_repeat_callers_threshold))); 135 | mRepeatCallers.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { 136 | @Override 137 | public boolean onPreferenceChange(Preference preference, Object newValue) { 138 | if (mDisableListeners) return true; 139 | final boolean val = (Boolean) newValue; 140 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ALLOW_REPEAT_CALLS, val); 141 | if (val == mConfig.allowRepeatCallers) return true; 142 | if (DEBUG) Log.d(TAG, "onPrefChange allowRepeatCallers=" + val); 143 | final ZenModeConfig newConfig = mConfig.copy(); 144 | newConfig.allowRepeatCallers = val; 145 | return setZenModeConfig(newConfig); 146 | } 147 | }); 148 | 149 | // Remove of the "Allow notification light" setting if LED is not supported 150 | if (!getResources().getBoolean( 151 | com.android.internal.R.bool.config_intrusiveNotificationLed)) { 152 | root.removePreference(findPreference(KEY_ALLOW_LIGHTS)); 153 | } 154 | 155 | updateControls(); 156 | } 157 | 158 | @Override 159 | protected void onZenModeChanged() { 160 | // don't care 161 | } 162 | 163 | @Override 164 | protected void onZenModeConfigChanged() { 165 | updateControls(); 166 | } 167 | 168 | private void updateControls() { 169 | mDisableListeners = true; 170 | if (mCalls != null) { 171 | mCalls.setSelectedValue(mConfig.allowCalls ? mConfig.allowCallsFrom : SOURCE_NONE); 172 | } 173 | mMessages.setSelectedValue(mConfig.allowMessages ? mConfig.allowMessagesFrom : SOURCE_NONE); 174 | mReminders.setChecked(mConfig.allowReminders); 175 | mEvents.setChecked(mConfig.allowEvents); 176 | mRepeatCallers.setChecked(mConfig.allowRepeatCallers); 177 | mRepeatCallers.setEnabled(!mConfig.allowCalls 178 | || mConfig.allowCallsFrom != ZenModeConfig.SOURCE_ANYONE); 179 | mDisableListeners = false; 180 | } 181 | 182 | @Override 183 | protected int getMetricsCategory() { 184 | return MetricsLogger.NOTIFICATION_ZEN_MODE_PRIORITY; 185 | } 186 | 187 | private static void addSources(DropDownPreference pref) { 188 | pref.addItem(R.string.zen_mode_from_anyone, ZenModeConfig.SOURCE_ANYONE); 189 | pref.addItem(R.string.zen_mode_from_contacts, ZenModeConfig.SOURCE_CONTACT); 190 | pref.addItem(R.string.zen_mode_from_starred, ZenModeConfig.SOURCE_STAR); 191 | pref.addItem(R.string.zen_mode_from_none, SOURCE_NONE); 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /notification/ZenRuleNameDialog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.app.AlertDialog; 20 | import android.content.ComponentName; 21 | import android.content.Context; 22 | import android.content.DialogInterface; 23 | import android.content.DialogInterface.OnDismissListener; 24 | import android.content.pm.ServiceInfo; 25 | import android.content.res.ColorStateList; 26 | import android.net.Uri; 27 | import android.service.notification.ZenModeConfig; 28 | import android.service.notification.ZenModeConfig.EventInfo; 29 | import android.service.notification.ZenModeConfig.ScheduleInfo; 30 | import android.text.Editable; 31 | import android.text.TextUtils; 32 | import android.text.TextWatcher; 33 | import android.util.ArraySet; 34 | import android.util.Log; 35 | import android.util.TypedValue; 36 | import android.view.LayoutInflater; 37 | import android.view.View; 38 | import android.widget.EditText; 39 | import android.widget.RadioButton; 40 | import android.widget.RadioGroup; 41 | 42 | import com.android.settings.R; 43 | 44 | import java.util.List; 45 | 46 | public abstract class ZenRuleNameDialog { 47 | private static final String TAG = ZenModeSettings.TAG; 48 | private static final boolean DEBUG = ZenModeSettings.DEBUG; 49 | 50 | private final AlertDialog mDialog; 51 | private final EditText mEditText; 52 | private final View mWarning; 53 | private final RadioGroup mTypes; 54 | private final ColorStateList mWarningTint; 55 | private final ColorStateList mOriginalTint; 56 | private final String mOriginalRuleName; 57 | private final ArraySet mExistingNames; 58 | private final ServiceListing mServiceListing; 59 | private final RuleInfo[] mExternalRules = new RuleInfo[3]; 60 | private final boolean mIsNew; 61 | 62 | public ZenRuleNameDialog(Context context, ServiceListing serviceListing, String ruleName, 63 | ArraySet existingNames) { 64 | mServiceListing = serviceListing; 65 | mIsNew = ruleName == null; 66 | mOriginalRuleName = ruleName; 67 | mWarningTint = ColorStateList.valueOf(context.getColor(R.color.zen_rule_name_warning)); 68 | final View v = LayoutInflater.from(context).inflate(R.layout.zen_rule_name, null, false); 69 | mEditText = (EditText) v.findViewById(R.id.rule_name); 70 | mWarning = v.findViewById(R.id.rule_name_warning); 71 | if (!mIsNew) { 72 | mEditText.setText(ruleName); 73 | } 74 | TypedValue outValue = new TypedValue(); 75 | context.getTheme().resolveAttribute(android.R.attr.colorAccent, outValue, true); 76 | mOriginalTint = ColorStateList.valueOf(outValue.data); 77 | mEditText.setSelectAllOnFocus(true); 78 | mTypes = (RadioGroup) v.findViewById(R.id.rule_types); 79 | if (mServiceListing != null) { 80 | bindType(R.id.rule_type_schedule, defaultNewSchedule()); 81 | bindType(R.id.rule_type_event, defaultNewEvent()); 82 | bindExternalRules(); 83 | mServiceListing.addCallback(mServiceListingCallback); 84 | mServiceListing.reload(); 85 | } else { 86 | mTypes.setVisibility(View.GONE); 87 | } 88 | mDialog = new AlertDialog.Builder(context) 89 | .setTitle(mIsNew ? R.string.zen_mode_add_rule : R.string.zen_mode_rule_name) 90 | .setView(v) 91 | .setPositiveButton(R.string.okay, new DialogInterface.OnClickListener() { 92 | @Override 93 | public void onClick(DialogInterface dialog, int which) { 94 | final String newName = trimmedText(); 95 | if (!mIsNew && mOriginalRuleName != null 96 | && mOriginalRuleName.equalsIgnoreCase(newName)) { 97 | return; // no change to an existing rule, just dismiss 98 | } 99 | onOk(newName, selectedRuleInfo()); 100 | } 101 | }) 102 | .setOnDismissListener(new OnDismissListener() { 103 | @Override 104 | public void onDismiss(DialogInterface dialog) { 105 | if (mServiceListing != null) { 106 | mServiceListing.removeCallback(mServiceListingCallback); 107 | } 108 | } 109 | }) 110 | .setNegativeButton(R.string.cancel, null) 111 | .create(); 112 | mEditText.addTextChangedListener(new TextWatcher() { 113 | @Override 114 | public void onTextChanged(CharSequence s, int start, int before, int count) { 115 | // noop 116 | } 117 | 118 | @Override 119 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 120 | // noop 121 | } 122 | 123 | @Override 124 | public void afterTextChanged(Editable s) { 125 | updatePositiveButtonAndWarning(); 126 | } 127 | }); 128 | mExistingNames = new ArraySet(existingNames.size()); 129 | for (String existingName : existingNames) { 130 | mExistingNames.add(existingName.toLowerCase()); 131 | } 132 | } 133 | 134 | abstract public void onOk(String ruleName, RuleInfo ruleInfo); 135 | 136 | public void show() { 137 | mDialog.show(); 138 | updatePositiveButtonAndWarning(); 139 | } 140 | 141 | private void bindType(int id, RuleInfo ri) { 142 | final RadioButton rb = (RadioButton) mTypes.findViewById(id); 143 | if (ri == null) { 144 | rb.setVisibility(View.GONE); 145 | return; 146 | } 147 | rb.setVisibility(View.VISIBLE); 148 | if (ri.caption != null) { 149 | rb.setText(ri.caption); 150 | } 151 | rb.setTag(ri); 152 | } 153 | 154 | private RuleInfo selectedRuleInfo() { 155 | final int id = mTypes.getCheckedRadioButtonId(); 156 | if (id == -1) return null; 157 | final RadioButton rb = (RadioButton) mTypes.findViewById(id); 158 | return (RuleInfo) rb.getTag(); 159 | } 160 | 161 | private String trimmedText() { 162 | return mEditText.getText() == null ? null : mEditText.getText().toString().trim(); 163 | } 164 | 165 | private void updatePositiveButtonAndWarning() { 166 | final String name = trimmedText(); 167 | final boolean validName = !TextUtils.isEmpty(name) 168 | && (name.equalsIgnoreCase(mOriginalRuleName) 169 | || !mExistingNames.contains(name.toLowerCase())); 170 | mDialog.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validName); 171 | final boolean showWarning = !TextUtils.isEmpty(name) && !validName; 172 | mWarning.setVisibility(showWarning ? View.VISIBLE : View.INVISIBLE); 173 | mEditText.setBackgroundTintList(showWarning ? mWarningTint : mOriginalTint); 174 | } 175 | 176 | private static RuleInfo defaultNewSchedule() { 177 | final ScheduleInfo schedule = new ScheduleInfo(); 178 | schedule.days = ZenModeConfig.ALL_DAYS; 179 | schedule.startHour = 22; 180 | schedule.endHour = 7; 181 | final RuleInfo rt = new RuleInfo(); 182 | rt.settingsAction = ZenModeScheduleRuleSettings.ACTION; 183 | rt.defaultConditionId = ZenModeConfig.toScheduleConditionId(schedule); 184 | return rt; 185 | } 186 | 187 | private static RuleInfo defaultNewEvent() { 188 | final EventInfo event = new EventInfo(); 189 | event.calendar = null; // any calendar 190 | event.reply = EventInfo.REPLY_ANY_EXCEPT_NO; 191 | final RuleInfo rt = new RuleInfo(); 192 | rt.settingsAction = ZenModeEventRuleSettings.ACTION; 193 | rt.defaultConditionId = ZenModeConfig.toEventConditionId(event); 194 | return rt; 195 | } 196 | 197 | private void bindExternalRules() { 198 | bindType(R.id.rule_type_3, mExternalRules[0]); 199 | bindType(R.id.rule_type_4, mExternalRules[1]); 200 | bindType(R.id.rule_type_5, mExternalRules[2]); 201 | } 202 | 203 | private final ServiceListing.Callback mServiceListingCallback = new ServiceListing.Callback() { 204 | @Override 205 | public void onServicesReloaded(List services) { 206 | if (DEBUG) Log.d(TAG, "Services reloaded: count=" + services.size()); 207 | mExternalRules[0] = mExternalRules[1] = mExternalRules[2] = null; 208 | int i = 0; 209 | for (ServiceInfo si : services) { 210 | final RuleInfo ri = ZenModeExternalRuleSettings.getRuleInfo(si); 211 | if (ri != null) { 212 | mExternalRules[i] = ri; 213 | i++; 214 | if (i == mExternalRules.length) { 215 | break; 216 | } 217 | } 218 | } 219 | bindExternalRules(); 220 | } 221 | }; 222 | 223 | public static class RuleInfo { 224 | public String caption; 225 | public String settingsAction; 226 | public Uri defaultConditionId; 227 | public ComponentName serviceComponent; 228 | public ComponentName configurationActivity; 229 | } 230 | 231 | } -------------------------------------------------------------------------------- /notificationlight/ApplicationLightPreference.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The CyanogenMod Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notificationlight; 18 | 19 | import android.app.AlertDialog; 20 | import android.app.Dialog; 21 | import android.content.Context; 22 | import android.content.DialogInterface; 23 | import android.content.res.Resources; 24 | import android.graphics.drawable.ShapeDrawable; 25 | import android.graphics.drawable.shapes.OvalShape; 26 | import android.os.Bundle; 27 | import android.preference.DialogPreference; 28 | import android.util.AttributeSet; 29 | import android.view.View; 30 | import android.widget.ImageView; 31 | import android.widget.TextView; 32 | 33 | import com.android.settings.R; 34 | 35 | public class ApplicationLightPreference extends DialogPreference { 36 | 37 | private static String TAG = "AppLightPreference"; 38 | public static final int DEFAULT_TIME = 1000; 39 | public static final int DEFAULT_COLOR = 0xffffff; 40 | 41 | private ImageView mLightColorView; 42 | private TextView mOnValueView; 43 | private TextView mOffValueView; 44 | 45 | private int mColorValue; 46 | private int mOnValue; 47 | private int mOffValue; 48 | private boolean mOnOffChangeable; 49 | 50 | private Resources mResources; 51 | 52 | /** 53 | * @param context 54 | * @param attrs 55 | */ 56 | public ApplicationLightPreference(Context context, AttributeSet attrs) { 57 | super(context, attrs); 58 | mColorValue = DEFAULT_COLOR; 59 | mOnValue = DEFAULT_TIME; 60 | mOffValue = DEFAULT_TIME; 61 | mOnOffChangeable = context.getResources().getBoolean( 62 | com.android.internal.R.bool.config_ledCanPulse); 63 | init(); 64 | } 65 | 66 | /** 67 | * @param context 68 | * @param color 69 | * @param onValue 70 | * @param offValue 71 | */ 72 | public ApplicationLightPreference(Context context, int color, int onValue, int offValue) { 73 | super(context, null); 74 | mColorValue = color; 75 | mOnValue = onValue; 76 | mOffValue = offValue; 77 | mOnOffChangeable = context.getResources().getBoolean( 78 | com.android.internal.R.bool.config_ledCanPulse); 79 | init(); 80 | } 81 | 82 | /** 83 | * @param context 84 | * @param color 85 | * @param onValue 86 | * @param offValue 87 | */ 88 | public ApplicationLightPreference(Context context, int color, int onValue, int offValue, boolean onOffChangeable) { 89 | super(context, null); 90 | mColorValue = color; 91 | mOnValue = onValue; 92 | mOffValue = offValue; 93 | mOnOffChangeable = onOffChangeable; 94 | init(); 95 | } 96 | 97 | private void init() { 98 | setLayoutResource(R.layout.preference_application_light); 99 | mResources = getContext().getResources(); 100 | } 101 | 102 | public void onStart() { 103 | LightSettingsDialog d = (LightSettingsDialog) getDialog(); 104 | if (d != null) { 105 | d.onStart(); 106 | } 107 | } 108 | 109 | public void onStop() { 110 | LightSettingsDialog d = (LightSettingsDialog) getDialog(); 111 | if (d != null) { 112 | d.onStop(); 113 | } 114 | } 115 | 116 | @Override 117 | protected void onBindView(View view) { 118 | super.onBindView(view); 119 | 120 | mLightColorView = (ImageView) view.findViewById(R.id.light_color); 121 | mOnValueView = (TextView) view.findViewById(R.id.textViewTimeOnValue); 122 | mOffValueView = (TextView) view.findViewById(R.id.textViewTimeOffValue); 123 | 124 | // Hide the summary text - it takes up too much space on a low res device 125 | // We use it for storing the package name for the longClickListener 126 | TextView tView = (TextView) view.findViewById(android.R.id.summary); 127 | tView.setVisibility(View.GONE); 128 | 129 | if (!mResources.getBoolean(com.android.internal.R.bool.config_multiColorNotificationLed)) { 130 | mLightColorView.setVisibility(View.GONE); 131 | } 132 | 133 | updatePreferenceViews(); 134 | } 135 | 136 | private void updatePreferenceViews() { 137 | final int size = (int) mResources.getDimension(R.dimen.oval_notification_size); 138 | 139 | if (mLightColorView != null) { 140 | mLightColorView.setEnabled(true); 141 | // adjust if necessary to prevent material whiteout 142 | final int imageColor = ((mColorValue & 0xF0F0F0) == 0xF0F0F0) ? 143 | (mColorValue - 0x101010) : mColorValue; 144 | mLightColorView.setImageDrawable(createOvalShape(size, 145 | 0xFF000000 + imageColor)); 146 | } 147 | if (mOnValueView != null) { 148 | mOnValueView.setText(mapLengthValue(mOnValue)); 149 | } 150 | if (mOffValueView != null) { 151 | if (mOnValue == 1 || !mOnOffChangeable) { 152 | mOffValueView.setVisibility(View.GONE); 153 | } else { 154 | mOffValueView.setVisibility(View.VISIBLE); 155 | } 156 | mOffValueView.setText(mapSpeedValue(mOffValue)); 157 | } 158 | } 159 | 160 | @Override 161 | protected void showDialog(Bundle state) { 162 | super.showDialog(state); 163 | 164 | final LightSettingsDialog d = (LightSettingsDialog) getDialog(); 165 | } 166 | 167 | @Override 168 | protected Dialog createDialog() { 169 | final LightSettingsDialog d = new LightSettingsDialog(getContext(), 170 | 0xFF000000 + mColorValue, mOnValue, mOffValue, mOnOffChangeable); 171 | d.setAlphaSliderVisible(false); 172 | 173 | d.setButton(AlertDialog.BUTTON_POSITIVE, mResources.getString(R.string.dlg_ok), 174 | new DialogInterface.OnClickListener() { 175 | @Override 176 | public void onClick(DialogInterface dialog, int which) { 177 | mColorValue = d.getColor() - 0xFF000000; // strip alpha, led does not support it 178 | mOnValue = d.getPulseSpeedOn(); 179 | mOffValue = d.getPulseSpeedOff(); 180 | updatePreferenceViews(); 181 | callChangeListener(this); 182 | } 183 | }); 184 | d.setButton(AlertDialog.BUTTON_NEGATIVE, mResources.getString(R.string.cancel), 185 | new DialogInterface.OnClickListener() { 186 | @Override 187 | public void onClick(DialogInterface dialog, int which) { 188 | } 189 | }); 190 | 191 | return d; 192 | } 193 | 194 | /** 195 | * Getters and Setters 196 | */ 197 | 198 | public int getColor() { 199 | return mColorValue; 200 | } 201 | 202 | public void setColor(int color) { 203 | mColorValue = color; 204 | updatePreferenceViews(); 205 | } 206 | 207 | public void setOnValue(int value) { 208 | mOnValue = value; 209 | updatePreferenceViews(); 210 | } 211 | 212 | public int getOnValue() { 213 | return mOnValue; 214 | } 215 | 216 | public void setOffValue(int value) { 217 | mOffValue = value; 218 | updatePreferenceViews(); 219 | } 220 | 221 | public int getOffValue() { 222 | return mOffValue; 223 | } 224 | 225 | public void setAllValues(int color, int onValue, int offValue) { 226 | mColorValue = color; 227 | mOnValue = onValue; 228 | mOffValue = offValue; 229 | updatePreferenceViews(); 230 | } 231 | 232 | public void setAllValues(int color, int onValue, int offValue, boolean onOffChangeable) { 233 | mColorValue = color; 234 | mOnValue = onValue; 235 | mOffValue = offValue; 236 | mOnOffChangeable = onOffChangeable; 237 | updatePreferenceViews(); 238 | } 239 | 240 | public void setOnOffValue(int onValue, int offValue) { 241 | mOnValue = onValue; 242 | mOffValue = offValue; 243 | updatePreferenceViews(); 244 | } 245 | 246 | public void setOnOffChangeable(boolean value) { 247 | mOnOffChangeable = value; 248 | } 249 | 250 | /** 251 | * Utility methods 252 | */ 253 | private static ShapeDrawable createOvalShape(int size, int color) { 254 | ShapeDrawable shape = new ShapeDrawable(new OvalShape()); 255 | shape.setIntrinsicHeight(size); 256 | shape.setIntrinsicWidth(size); 257 | shape.getPaint().setColor(color); 258 | return shape; 259 | } 260 | 261 | private String mapLengthValue(Integer time) { 262 | if (!mOnOffChangeable) { 263 | return getContext().getString(R.string.pulse_length_always_on); 264 | } 265 | if (time == DEFAULT_TIME) { 266 | return getContext().getString(R.string.default_time); 267 | } 268 | 269 | String[] timeNames = mResources.getStringArray(R.array.notification_pulse_length_entries); 270 | String[] timeValues = mResources.getStringArray(R.array.notification_pulse_length_values); 271 | 272 | for (int i = 0; i < timeValues.length; i++) { 273 | if (Integer.decode(timeValues[i]).equals(time)) { 274 | return timeNames[i]; 275 | } 276 | } 277 | 278 | return getContext().getString(R.string.custom_time); 279 | } 280 | 281 | private String mapSpeedValue(Integer time) { 282 | if (time == DEFAULT_TIME) { 283 | return getContext().getString(R.string.default_time); 284 | } 285 | 286 | String[] timeNames = mResources.getStringArray(R.array.notification_pulse_speed_entries); 287 | String[] timeValues = mResources.getStringArray(R.array.notification_pulse_speed_values); 288 | 289 | for (int i = 0; i < timeValues.length; i++) { 290 | if (Integer.decode(timeValues[i]).equals(time)) { 291 | return timeNames[i]; 292 | } 293 | } 294 | 295 | return getContext().getString(R.string.custom_time); 296 | } 297 | } 298 | -------------------------------------------------------------------------------- /notification/ZenModeRuleSettingsBase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.app.AlertDialog; 20 | import android.content.Context; 21 | import android.content.DialogInterface; 22 | import android.content.DialogInterface.OnClickListener; 23 | import android.content.Intent; 24 | import android.net.Uri; 25 | import android.os.Bundle; 26 | import android.preference.Preference; 27 | import android.preference.Preference.OnPreferenceClickListener; 28 | import android.preference.PreferenceScreen; 29 | import android.provider.Settings.Global; 30 | import android.service.notification.ZenModeConfig; 31 | import android.service.notification.ZenModeConfig.ZenRule; 32 | import android.util.Log; 33 | import android.view.Menu; 34 | import android.view.MenuInflater; 35 | import android.view.MenuItem; 36 | import android.view.View; 37 | import android.widget.Switch; 38 | import android.widget.TextView; 39 | import android.widget.Toast; 40 | 41 | import com.android.internal.logging.MetricsLogger; 42 | import com.android.settings.DropDownPreference; 43 | import com.android.settings.R; 44 | import com.android.settings.SettingsActivity; 45 | import com.android.settings.widget.SwitchBar; 46 | 47 | public abstract class ZenModeRuleSettingsBase extends ZenModeSettingsBase 48 | implements SwitchBar.OnSwitchChangeListener { 49 | protected static final String TAG = ZenModeSettingsBase.TAG; 50 | protected static final boolean DEBUG = ZenModeSettingsBase.DEBUG; 51 | 52 | public static final String EXTRA_RULE_ID = "rule_id"; 53 | private static final String KEY_RULE_NAME = "rule_name"; 54 | private static final String KEY_ZEN_MODE = "zen_mode"; 55 | 56 | protected Context mContext; 57 | protected boolean mDisableListeners; 58 | protected ZenRule mRule; 59 | 60 | private String mRuleId; 61 | private boolean mDeleting; 62 | private Preference mRuleName; 63 | private SwitchBar mSwitchBar; 64 | private DropDownPreference mZenMode; 65 | private Toast mEnabledToast; 66 | 67 | abstract protected void onCreateInternal(); 68 | abstract protected boolean setRule(ZenRule rule); 69 | abstract protected String getZenModeDependency(); 70 | abstract protected void updateControlsInternal(); 71 | abstract protected int getEnabledToastText(); 72 | 73 | @Override 74 | public void onCreate(Bundle icicle) { 75 | super.onCreate(icicle); 76 | 77 | mContext = getActivity(); 78 | 79 | final Intent intent = getActivity().getIntent(); 80 | if (DEBUG) Log.d(TAG, "onCreate getIntent()=" + intent); 81 | if (intent == null) { 82 | Log.w(TAG, "No intent"); 83 | toastAndFinish(); 84 | return; 85 | } 86 | 87 | mRuleId = intent.getStringExtra(EXTRA_RULE_ID); 88 | if (DEBUG) Log.d(TAG, "mRuleId=" + mRuleId); 89 | if (refreshRuleOrFinish()) { 90 | return; 91 | } 92 | 93 | setHasOptionsMenu(true); 94 | 95 | onCreateInternal(); 96 | 97 | final PreferenceScreen root = getPreferenceScreen(); 98 | mRuleName = root.findPreference(KEY_RULE_NAME); 99 | mRuleName.setOnPreferenceClickListener(new OnPreferenceClickListener() { 100 | @Override 101 | public boolean onPreferenceClick(Preference preference) { 102 | showRuleNameDialog(); 103 | return true; 104 | } 105 | }); 106 | 107 | mZenMode = (DropDownPreference) root.findPreference(KEY_ZEN_MODE); 108 | mZenMode.addItem(R.string.zen_mode_option_important_interruptions, 109 | Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); 110 | mZenMode.addItem(R.string.zen_mode_option_alarms, Global.ZEN_MODE_ALARMS); 111 | mZenMode.addItem(R.string.zen_mode_option_no_interruptions, 112 | Global.ZEN_MODE_NO_INTERRUPTIONS); 113 | mZenMode.setCallback(new DropDownPreference.Callback() { 114 | @Override 115 | public boolean onItemSelected(int pos, Object value) { 116 | if (mDisableListeners) return true; 117 | final int zenMode = (Integer) value; 118 | if (zenMode == mRule.zenMode) return true; 119 | if (DEBUG) Log.d(TAG, "onPrefChange zenMode=" + zenMode); 120 | mRule.zenMode = zenMode; 121 | setZenModeConfig(mConfig); 122 | return true; 123 | } 124 | }); 125 | mZenMode.setOrder(10); // sort at the bottom of the category 126 | mZenMode.setDependency(getZenModeDependency()); 127 | } 128 | 129 | @Override 130 | public void onResume() { 131 | super.onResume(); 132 | updateControls(); 133 | } 134 | 135 | @Override 136 | public void onActivityCreated(Bundle savedInstanceState) { 137 | super.onActivityCreated(savedInstanceState); 138 | 139 | final SettingsActivity activity = (SettingsActivity) getActivity(); 140 | mSwitchBar = activity.getSwitchBar(); 141 | mSwitchBar.addOnSwitchChangeListener(this); 142 | mSwitchBar.show(); 143 | } 144 | 145 | @Override 146 | public void onDestroyView() { 147 | super.onDestroyView(); 148 | mSwitchBar.removeOnSwitchChangeListener(this); 149 | mSwitchBar.hide(); 150 | } 151 | 152 | @Override 153 | public void onSwitchChanged(Switch switchView, boolean isChecked) { 154 | if (DEBUG) Log.d(TAG, "onSwitchChanged " + isChecked); 155 | if (mDisableListeners) return; 156 | final boolean enabled = isChecked; 157 | if (enabled == mRule.enabled) return; 158 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ENABLE_RULE, enabled); 159 | if (DEBUG) Log.d(TAG, "onSwitchChanged enabled=" + enabled); 160 | mRule.enabled = enabled; 161 | mRule.snoozing = false; 162 | setZenModeConfig(mConfig); 163 | if (enabled) { 164 | final int toastText = getEnabledToastText(); 165 | if (toastText != 0) { 166 | mEnabledToast = Toast.makeText(mContext, toastText, Toast.LENGTH_SHORT); 167 | mEnabledToast.show(); 168 | } 169 | } else { 170 | if (mEnabledToast != null) { 171 | mEnabledToast.cancel(); 172 | } 173 | } 174 | } 175 | 176 | protected void updateRule(Uri newConditionId) { 177 | mRule.conditionId = newConditionId; 178 | mRule.condition = null; 179 | mRule.snoozing = false; 180 | setZenModeConfig(mConfig); 181 | } 182 | 183 | @Override 184 | protected void onZenModeChanged() { 185 | // noop 186 | } 187 | 188 | @Override 189 | protected void onZenModeConfigChanged() { 190 | if (!refreshRuleOrFinish()) { 191 | updateControls(); 192 | } 193 | } 194 | 195 | @Override 196 | public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 197 | if (DEBUG) Log.d(TAG, "onCreateOptionsMenu"); 198 | inflater.inflate(R.menu.zen_mode_rule, menu); 199 | } 200 | 201 | @Override 202 | public boolean onOptionsItemSelected(MenuItem item) { 203 | if (DEBUG) Log.d(TAG, "onOptionsItemSelected " + item.getItemId()); 204 | if (item.getItemId() == R.id.delete) { 205 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_DELETE_RULE); 206 | showDeleteRuleDialog(); 207 | return true; 208 | } 209 | return super.onOptionsItemSelected(item); 210 | } 211 | 212 | private void showRuleNameDialog() { 213 | new ZenRuleNameDialog(mContext, null, mRule.name, mConfig.getAutomaticRuleNames()) { 214 | @Override 215 | public void onOk(String ruleName, RuleInfo type) { 216 | final ZenModeConfig newConfig = mConfig.copy(); 217 | final ZenRule rule = newConfig.automaticRules.get(mRuleId); 218 | if (rule == null) return; 219 | rule.name = ruleName; 220 | setZenModeConfig(newConfig); 221 | } 222 | }.show(); 223 | } 224 | 225 | private boolean refreshRuleOrFinish() { 226 | mRule = mConfig.automaticRules.get(mRuleId); 227 | if (DEBUG) Log.d(TAG, "mRule=" + mRule); 228 | if (!setRule(mRule)) { 229 | toastAndFinish(); 230 | return true; 231 | } 232 | return false; 233 | } 234 | 235 | private void showDeleteRuleDialog() { 236 | final AlertDialog dialog = new AlertDialog.Builder(mContext) 237 | .setMessage(getString(R.string.zen_mode_delete_rule_confirmation, mRule.name)) 238 | .setNegativeButton(R.string.cancel, null) 239 | .setPositiveButton(R.string.zen_mode_delete_rule_button, new OnClickListener() { 240 | @Override 241 | public void onClick(DialogInterface dialog, int which) { 242 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_DELETE_RULE_OK); 243 | mDeleting = true; 244 | mConfig.automaticRules.remove(mRuleId); 245 | setZenModeConfig(mConfig); 246 | } 247 | }) 248 | .show(); 249 | final View messageView = dialog.findViewById(android.R.id.message); 250 | if (messageView != null) { 251 | messageView.setTextDirection(View.TEXT_DIRECTION_LOCALE); 252 | } 253 | } 254 | 255 | private void toastAndFinish() { 256 | if (!mDeleting) { 257 | Toast.makeText(mContext, R.string.zen_mode_rule_not_found_text, Toast.LENGTH_SHORT) 258 | .show(); 259 | } 260 | getActivity().finish(); 261 | } 262 | 263 | private void updateRuleName() { 264 | getActivity().setTitle(mRule.name); 265 | mRuleName.setSummary(mRule.name); 266 | } 267 | 268 | private void updateControls() { 269 | mDisableListeners = true; 270 | updateRuleName(); 271 | updateControlsInternal(); 272 | mZenMode.setSelectedValue(mRule.zenMode); 273 | if (mSwitchBar != null) { 274 | mSwitchBar.setChecked(mRule.enabled); 275 | } 276 | mDisableListeners = false; 277 | } 278 | 279 | } 280 | -------------------------------------------------------------------------------- /notification/ZenModeScheduleRuleSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import static com.android.settings.notification.ZenModeScheduleDaysSelection.DAYS; 20 | 21 | import android.app.AlertDialog; 22 | import android.app.Dialog; 23 | import android.app.DialogFragment; 24 | import android.app.FragmentManager; 25 | import android.app.TimePickerDialog; 26 | import android.content.Context; 27 | import android.content.DialogInterface; 28 | import android.content.DialogInterface.OnDismissListener; 29 | import android.os.Bundle; 30 | import android.preference.Preference; 31 | import android.preference.Preference.OnPreferenceClickListener; 32 | import android.preference.PreferenceScreen; 33 | import android.provider.Settings; 34 | import android.service.notification.ZenModeConfig; 35 | import android.service.notification.ZenModeConfig.ScheduleInfo; 36 | import android.service.notification.ZenModeConfig.ZenRule; 37 | import android.text.format.DateFormat; 38 | import android.util.Log; 39 | import android.widget.TimePicker; 40 | 41 | import com.android.internal.logging.MetricsLogger; 42 | import com.android.settings.R; 43 | 44 | import java.text.SimpleDateFormat; 45 | import java.util.Arrays; 46 | import java.util.Calendar; 47 | 48 | public class ZenModeScheduleRuleSettings extends ZenModeRuleSettingsBase { 49 | private static final String KEY_DAYS = "days"; 50 | private static final String KEY_START_TIME = "start_time"; 51 | private static final String KEY_END_TIME = "end_time"; 52 | 53 | public static final String ACTION = Settings.ACTION_ZEN_MODE_SCHEDULE_RULE_SETTINGS; 54 | 55 | // per-instance to ensure we're always using the current locale 56 | private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE"); 57 | 58 | private Preference mDays; 59 | private TimePickerPreference mStart; 60 | private TimePickerPreference mEnd; 61 | 62 | private ScheduleInfo mSchedule; 63 | 64 | @Override 65 | protected boolean setRule(ZenRule rule) { 66 | mSchedule = rule != null ? ZenModeConfig.tryParseScheduleConditionId(rule.conditionId) 67 | : null; 68 | return mSchedule != null; 69 | } 70 | 71 | @Override 72 | protected String getZenModeDependency() { 73 | return mDays.getKey(); 74 | } 75 | 76 | @Override 77 | protected int getEnabledToastText() { 78 | return R.string.zen_schedule_rule_enabled_toast; 79 | } 80 | 81 | @Override 82 | protected void onCreateInternal() { 83 | addPreferencesFromResource(R.xml.zen_mode_schedule_rule_settings); 84 | final PreferenceScreen root = getPreferenceScreen(); 85 | 86 | mDays = root.findPreference(KEY_DAYS); 87 | mDays.setOnPreferenceClickListener(new OnPreferenceClickListener() { 88 | @Override 89 | public boolean onPreferenceClick(Preference preference) { 90 | showDaysDialog(); 91 | return true; 92 | } 93 | }); 94 | 95 | final FragmentManager mgr = getFragmentManager(); 96 | 97 | mStart = new TimePickerPreference(mContext, mgr); 98 | mStart.setKey(KEY_START_TIME); 99 | mStart.setTitle(R.string.zen_mode_start_time); 100 | mStart.setCallback(new TimePickerPreference.Callback() { 101 | @Override 102 | public boolean onSetTime(final int hour, final int minute) { 103 | if (mDisableListeners) return true; 104 | if (!ZenModeConfig.isValidHour(hour)) return false; 105 | if (!ZenModeConfig.isValidMinute(minute)) return false; 106 | if (hour == mSchedule.startHour && minute == mSchedule.startMinute) { 107 | return true; 108 | } 109 | if (DEBUG) Log.d(TAG, "onPrefChange start h=" + hour + " m=" + minute); 110 | mSchedule.startHour = hour; 111 | mSchedule.startMinute = minute; 112 | updateRule(ZenModeConfig.toScheduleConditionId(mSchedule)); 113 | return true; 114 | } 115 | }); 116 | root.addPreference(mStart); 117 | mStart.setDependency(mDays.getKey()); 118 | 119 | mEnd = new TimePickerPreference(mContext, mgr); 120 | mEnd.setKey(KEY_END_TIME); 121 | mEnd.setTitle(R.string.zen_mode_end_time); 122 | mEnd.setCallback(new TimePickerPreference.Callback() { 123 | @Override 124 | public boolean onSetTime(final int hour, final int minute) { 125 | if (mDisableListeners) return true; 126 | if (!ZenModeConfig.isValidHour(hour)) return false; 127 | if (!ZenModeConfig.isValidMinute(minute)) return false; 128 | if (hour == mSchedule.endHour && minute == mSchedule.endMinute) { 129 | return true; 130 | } 131 | if (DEBUG) Log.d(TAG, "onPrefChange end h=" + hour + " m=" + minute); 132 | mSchedule.endHour = hour; 133 | mSchedule.endMinute = minute; 134 | updateRule(ZenModeConfig.toScheduleConditionId(mSchedule)); 135 | return true; 136 | } 137 | }); 138 | root.addPreference(mEnd); 139 | mEnd.setDependency(mDays.getKey()); 140 | } 141 | 142 | private void updateDays() { 143 | // Compute an ordered, delimited list of day names based on the persisted user config. 144 | final int[] days = mSchedule.days; 145 | if (days != null && days.length > 0) { 146 | final StringBuilder sb = new StringBuilder(); 147 | final Calendar c = Calendar.getInstance(); 148 | for (int i = 0; i < DAYS.length; i++) { 149 | final int day = DAYS[i]; 150 | for (int j = 0; j < days.length; j++) { 151 | if (day == days[j]) { 152 | c.set(Calendar.DAY_OF_WEEK, day); 153 | if (sb.length() > 0) { 154 | sb.append(mContext.getString(R.string.summary_divider_text)); 155 | } 156 | sb.append(mDayFormat.format(c.getTime())); 157 | break; 158 | } 159 | } 160 | } 161 | if (sb.length() > 0) { 162 | mDays.setSummary(sb); 163 | mDays.notifyDependencyChange(false); 164 | return; 165 | } 166 | } 167 | mDays.setSummary(R.string.zen_mode_schedule_rule_days_none); 168 | mDays.notifyDependencyChange(true); 169 | } 170 | 171 | private void updateEndSummary() { 172 | final int startMin = 60 * mSchedule.startHour + mSchedule.startMinute; 173 | final int endMin = 60 * mSchedule.endHour + mSchedule.endMinute; 174 | final boolean nextDay = startMin >= endMin; 175 | final int summaryFormat = nextDay ? R.string.zen_mode_end_time_next_day_summary_format : 0; 176 | mEnd.setSummaryFormat(summaryFormat); 177 | } 178 | 179 | @Override 180 | protected void updateControlsInternal() { 181 | updateDays(); 182 | mStart.setTime(mSchedule.startHour, mSchedule.startMinute); 183 | mEnd.setTime(mSchedule.endHour, mSchedule.endMinute); 184 | updateEndSummary(); 185 | } 186 | 187 | @Override 188 | protected int getMetricsCategory() { 189 | return MetricsLogger.NOTIFICATION_ZEN_MODE_SCHEDULE_RULE; 190 | } 191 | 192 | private void showDaysDialog() { 193 | new AlertDialog.Builder(mContext) 194 | .setTitle(R.string.zen_mode_schedule_rule_days) 195 | .setView(new ZenModeScheduleDaysSelection(mContext, mSchedule.days) { 196 | @Override 197 | protected void onChanged(final int[] days) { 198 | if (mDisableListeners) return; 199 | if (Arrays.equals(days, mSchedule.days)) return; 200 | if (DEBUG) Log.d(TAG, "days.onChanged days=" + Arrays.asList(days)); 201 | mSchedule.days = days; 202 | updateRule(ZenModeConfig.toScheduleConditionId(mSchedule)); 203 | } 204 | }) 205 | .setOnDismissListener(new OnDismissListener() { 206 | @Override 207 | public void onDismiss(DialogInterface dialog) { 208 | updateDays(); 209 | } 210 | }) 211 | .setPositiveButton(R.string.done_button, null) 212 | .show(); 213 | } 214 | 215 | private static class TimePickerPreference extends Preference { 216 | private final Context mContext; 217 | 218 | private int mSummaryFormat; 219 | private int mHourOfDay; 220 | private int mMinute; 221 | private Callback mCallback; 222 | 223 | public TimePickerPreference(Context context, final FragmentManager mgr) { 224 | super(context); 225 | mContext = context; 226 | setPersistent(false); 227 | setOnPreferenceClickListener(new OnPreferenceClickListener(){ 228 | @Override 229 | public boolean onPreferenceClick(Preference preference) { 230 | final TimePickerFragment frag = new TimePickerFragment(); 231 | frag.pref = TimePickerPreference.this; 232 | frag.show(mgr, TimePickerPreference.class.getName()); 233 | return true; 234 | } 235 | }); 236 | } 237 | 238 | public void setCallback(Callback callback) { 239 | mCallback = callback; 240 | } 241 | 242 | public void setSummaryFormat(int resId) { 243 | mSummaryFormat = resId; 244 | updateSummary(); 245 | } 246 | 247 | public void setTime(int hourOfDay, int minute) { 248 | if (mCallback != null && !mCallback.onSetTime(hourOfDay, minute)) return; 249 | mHourOfDay = hourOfDay; 250 | mMinute = minute; 251 | updateSummary(); 252 | } 253 | 254 | private void updateSummary() { 255 | final Calendar c = Calendar.getInstance(); 256 | c.set(Calendar.HOUR_OF_DAY, mHourOfDay); 257 | c.set(Calendar.MINUTE, mMinute); 258 | String time = DateFormat.getTimeFormat(mContext).format(c.getTime()); 259 | if (mSummaryFormat != 0) { 260 | time = mContext.getResources().getString(mSummaryFormat, time); 261 | } 262 | setSummary(time); 263 | } 264 | 265 | public static class TimePickerFragment extends DialogFragment implements 266 | TimePickerDialog.OnTimeSetListener { 267 | public TimePickerPreference pref; 268 | 269 | @Override 270 | public Dialog onCreateDialog(Bundle savedInstanceState) { 271 | final boolean usePref = pref != null && pref.mHourOfDay >= 0 && pref.mMinute >= 0; 272 | final Calendar c = Calendar.getInstance(); 273 | final int hour = usePref ? pref.mHourOfDay : c.get(Calendar.HOUR_OF_DAY); 274 | final int minute = usePref ? pref.mMinute : c.get(Calendar.MINUTE); 275 | return new TimePickerDialog(getActivity(), this, hour, minute, 276 | DateFormat.is24HourFormat(getActivity())); 277 | } 278 | 279 | public void onTimeSet(TimePicker view, int hourOfDay, int minute) { 280 | if (pref != null) { 281 | pref.setTime(hourOfDay, minute); 282 | } 283 | } 284 | } 285 | 286 | public interface Callback { 287 | boolean onSetTime(int hour, int minute); 288 | } 289 | } 290 | 291 | } 292 | -------------------------------------------------------------------------------- /notification/NotificationStation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2012 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import android.app.Activity; 20 | import android.app.ActivityManager; 21 | import android.app.INotificationManager; 22 | import android.app.Notification; 23 | import android.content.ComponentName; 24 | import android.content.Context; 25 | import android.content.Intent; 26 | import android.content.pm.ApplicationInfo; 27 | import android.content.pm.PackageManager; 28 | import android.content.res.Resources; 29 | import android.graphics.drawable.Drawable; 30 | import android.graphics.ColorFilter; 31 | import android.graphics.LightingColorFilter; 32 | import android.net.Uri; 33 | import android.os.Bundle; 34 | import android.os.Handler; 35 | import android.os.RemoteException; 36 | import android.os.ServiceManager; 37 | import android.os.UserHandle; 38 | import android.service.notification.NotificationListenerService; 39 | import android.service.notification.StatusBarNotification; 40 | import android.util.Log; 41 | import android.view.LayoutInflater; 42 | import android.view.View; 43 | import android.view.View.OnClickListener; 44 | import android.view.ViewGroup; 45 | import android.widget.ArrayAdapter; 46 | import android.widget.DateTimeView; 47 | import android.widget.ImageView; 48 | import android.widget.ListView; 49 | import android.widget.TextView; 50 | 51 | import com.android.internal.logging.MetricsLogger; 52 | import com.android.settings.R; 53 | import com.android.settings.SettingsPreferenceFragment; 54 | import com.android.settings.Utils; 55 | 56 | import java.util.ArrayList; 57 | import java.util.Comparator; 58 | import java.util.List; 59 | 60 | public class NotificationStation extends SettingsPreferenceFragment { 61 | private static final String TAG = NotificationStation.class.getSimpleName(); 62 | 63 | private static final boolean DEBUG = false; 64 | 65 | private static class HistoricalNotificationInfo { 66 | public String pkg; 67 | public Drawable pkgicon; 68 | public CharSequence pkgname; 69 | public Drawable icon; 70 | public CharSequence title; 71 | public int priority; 72 | public int user; 73 | public long timestamp; 74 | public boolean active; 75 | } 76 | 77 | private PackageManager mPm; 78 | private INotificationManager mNoMan; 79 | private ColorFilter mFilter; 80 | 81 | private Runnable mRefreshListRunnable = new Runnable() { 82 | @Override 83 | public void run() { 84 | refreshList(); 85 | } 86 | }; 87 | 88 | private NotificationListenerService mListener = new NotificationListenerService() { 89 | @Override 90 | public void onNotificationPosted(StatusBarNotification notification) { 91 | logd("onNotificationPosted: %s", notification); 92 | final Handler h = getListView().getHandler(); 93 | h.removeCallbacks(mRefreshListRunnable); 94 | h.postDelayed(mRefreshListRunnable, 100); 95 | } 96 | 97 | @Override 98 | public void onNotificationRemoved(StatusBarNotification notification) { 99 | final Handler h = getListView().getHandler(); 100 | h.removeCallbacks(mRefreshListRunnable); 101 | h.postDelayed(mRefreshListRunnable, 100); 102 | } 103 | }; 104 | 105 | private NotificationHistoryAdapter mAdapter; 106 | private Context mContext; 107 | 108 | private final Comparator mNotificationSorter 109 | = new Comparator() { 110 | @Override 111 | public int compare(HistoricalNotificationInfo lhs, 112 | HistoricalNotificationInfo rhs) { 113 | return (int)(rhs.timestamp - lhs.timestamp); 114 | } 115 | }; 116 | 117 | @Override 118 | public void onAttach(Activity activity) { 119 | logd("onAttach(%s)", activity.getClass().getSimpleName()); 120 | super.onAttach(activity); 121 | mContext = activity; 122 | mPm = mContext.getPackageManager(); 123 | mNoMan = INotificationManager.Stub.asInterface( 124 | ServiceManager.getService(Context.NOTIFICATION_SERVICE)); 125 | try { 126 | mListener.registerAsSystemService(mContext, new ComponentName(mContext.getPackageName(), 127 | this.getClass().getCanonicalName()), ActivityManager.getCurrentUser()); 128 | } catch (RemoteException e) { 129 | Log.e(TAG, "Cannot register listener", e); 130 | } 131 | } 132 | 133 | @Override 134 | public void onCreate(Bundle savedInstanceState) { 135 | logd("onCreate(%s)", savedInstanceState); 136 | super.onCreate(savedInstanceState); 137 | 138 | int colorPrimaryDark = getResources().getColor(R.color.theme_primary_dark); 139 | mFilter = new LightingColorFilter(colorPrimaryDark, colorPrimaryDark); 140 | } 141 | 142 | @Override 143 | public void onDetach() { 144 | try { 145 | mListener.unregisterAsSystemService(); 146 | } catch (RemoteException e) { 147 | Log.e(TAG, "Cannot unregister listener", e); 148 | } 149 | super.onDetach(); 150 | } 151 | 152 | @Override 153 | protected int getMetricsCategory() { 154 | return MetricsLogger.NOTIFICATION_STATION; 155 | } 156 | 157 | @Override 158 | public void onActivityCreated(Bundle savedInstanceState) { 159 | logd("onActivityCreated(%s)", savedInstanceState); 160 | super.onActivityCreated(savedInstanceState); 161 | 162 | ListView listView = getListView(); 163 | Utils.forceCustomPadding(listView, false /* non additive padding */); 164 | 165 | mAdapter = new NotificationHistoryAdapter(mContext); 166 | listView.setAdapter(mAdapter); 167 | } 168 | 169 | @Override 170 | public void onResume() { 171 | logd("onResume()"); 172 | super.onResume(); 173 | refreshList(); 174 | } 175 | 176 | private void refreshList() { 177 | List infos = loadNotifications(); 178 | if (infos != null) { 179 | logd("adding %d infos", infos.size()); 180 | mAdapter.clear(); 181 | mAdapter.addAll(infos); 182 | mAdapter.sort(mNotificationSorter); 183 | } 184 | } 185 | 186 | private static void logd(String msg, Object... args) { 187 | if (DEBUG) { 188 | Log.d(TAG, args == null || args.length == 0 ? msg : String.format(msg, args)); 189 | } 190 | } 191 | 192 | private List loadNotifications() { 193 | final int currentUserId = ActivityManager.getCurrentUser(); 194 | try { 195 | StatusBarNotification[] active = mNoMan.getActiveNotifications( 196 | mContext.getPackageName()); 197 | StatusBarNotification[] dismissed = mNoMan.getHistoricalNotifications( 198 | mContext.getPackageName(), 50); 199 | 200 | List list 201 | = new ArrayList(active.length + dismissed.length); 202 | 203 | for (StatusBarNotification[] resultset 204 | : new StatusBarNotification[][] { active, dismissed }) { 205 | for (StatusBarNotification sbn : resultset) { 206 | final HistoricalNotificationInfo info = new HistoricalNotificationInfo(); 207 | info.pkg = sbn.getPackageName(); 208 | info.user = sbn.getUserId(); 209 | info.icon = loadIconDrawable(info.pkg, info.user, sbn.getNotification().icon); 210 | info.pkgicon = loadPackageIconDrawable(info.pkg, info.user); 211 | info.pkgname = loadPackageName(info.pkg); 212 | if (sbn.getNotification().extras != null) { 213 | info.title = sbn.getNotification().extras.getString( 214 | Notification.EXTRA_TITLE); 215 | if (info.title == null || "".equals(info.title)) { 216 | info.title = sbn.getNotification().extras.getString( 217 | Notification.EXTRA_TEXT); 218 | } 219 | } 220 | if (info.title == null || "".equals(info.title)) { 221 | info.title = sbn.getNotification().tickerText; 222 | } 223 | // still nothing? come on, give us something! 224 | if (info.title == null || "".equals(info.title)) { 225 | info.title = info.pkgname; 226 | } 227 | info.timestamp = sbn.getPostTime(); 228 | info.priority = sbn.getNotification().priority; 229 | logd(" [%d] %s: %s", info.timestamp, info.pkg, info.title); 230 | 231 | info.active = (resultset == active); 232 | 233 | if (info.user == UserHandle.USER_ALL 234 | || info.user == currentUserId) { 235 | list.add(info); 236 | } 237 | } 238 | } 239 | 240 | return list; 241 | } catch (RemoteException e) { 242 | Log.e(TAG, "Cannot load Notifications: ", e); 243 | } 244 | return null; 245 | } 246 | 247 | private Resources getResourcesForUserPackage(String pkg, int userId) { 248 | Resources r = null; 249 | 250 | if (pkg != null) { 251 | try { 252 | if (userId == UserHandle.USER_ALL) { 253 | userId = UserHandle.USER_OWNER; 254 | } 255 | r = mPm.getResourcesForApplicationAsUser(pkg, userId); 256 | } catch (PackageManager.NameNotFoundException ex) { 257 | Log.e(TAG, "Icon package not found: " + pkg, ex); 258 | return null; 259 | } 260 | } else { 261 | r = mContext.getResources(); 262 | } 263 | return r; 264 | } 265 | 266 | private Drawable loadPackageIconDrawable(String pkg, int userId) { 267 | Drawable icon = null; 268 | try { 269 | icon = mPm.getApplicationIcon(pkg); 270 | } catch (PackageManager.NameNotFoundException e) { 271 | Log.e(TAG, "Cannot get application icon", e); 272 | } 273 | 274 | return icon; 275 | } 276 | 277 | private CharSequence loadPackageName(String pkg) { 278 | try { 279 | ApplicationInfo info = mPm.getApplicationInfo(pkg, 280 | PackageManager.GET_UNINSTALLED_PACKAGES); 281 | if (info != null) return mPm.getApplicationLabel(info); 282 | } catch (PackageManager.NameNotFoundException e) { 283 | Log.e(TAG, "Cannot load package name", e); 284 | } 285 | return pkg; 286 | } 287 | 288 | private Drawable loadIconDrawable(String pkg, int userId, int resId) { 289 | Resources r = getResourcesForUserPackage(pkg, userId); 290 | Drawable d; 291 | 292 | if (resId == 0) { 293 | return null; 294 | } 295 | 296 | try { 297 | d = r.getDrawable(resId, null).mutate(); 298 | d.setColorFilter(mFilter); 299 | return d; 300 | } catch (RuntimeException e) { 301 | Log.w(TAG, "Icon not found in " 302 | + (pkg != null ? resId : "") 303 | + ": " + Integer.toHexString(resId), e); 304 | } 305 | 306 | return null; 307 | } 308 | 309 | private class NotificationHistoryAdapter extends ArrayAdapter { 310 | private final LayoutInflater mInflater; 311 | 312 | public NotificationHistoryAdapter(Context context) { 313 | super(context, 0); 314 | mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 315 | } 316 | 317 | @Override 318 | public View getView(int position, View convertView, ViewGroup parent) { 319 | final HistoricalNotificationInfo info = getItem(position); 320 | logd("getView(%s/%s)", info.pkg, info.title); 321 | 322 | final View row = convertView != null ? convertView : createRow(parent); 323 | row.setTag(info); 324 | 325 | // bind icon 326 | if (info.icon != null) { 327 | ((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(info.icon); 328 | } 329 | if (info.pkgicon != null) { 330 | ((ImageView) row.findViewById(R.id.pkgicon)).setImageDrawable(info.pkgicon); 331 | } 332 | 333 | ((DateTimeView) row.findViewById(R.id.timestamp)).setTime(info.timestamp); 334 | ((TextView) row.findViewById(android.R.id.title)).setText(info.title); 335 | ((TextView) row.findViewById(R.id.pkgname)).setText(info.pkgname); 336 | 337 | row.findViewById(R.id.extra).setVisibility(View.GONE); 338 | row.setAlpha(info.active ? 1.0f : 0.5f); 339 | 340 | // set up click handler 341 | row.setOnClickListener(new OnClickListener(){ 342 | @Override 343 | public void onClick(View v) { 344 | v.setPressed(true); 345 | startApplicationDetailsActivity(info.pkg); 346 | }}); 347 | 348 | return row; 349 | } 350 | 351 | private View createRow(ViewGroup parent) { 352 | return mInflater.inflate(R.layout.notification_log_row, parent, false); 353 | } 354 | 355 | } 356 | 357 | private void startApplicationDetailsActivity(String packageName) { 358 | Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, 359 | Uri.fromParts("package", packageName, null)); 360 | intent.setComponent(intent.resolveActivity(mPm)); 361 | startActivity(intent); 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /notification/ZenModeAutomationSettings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.android.settings.notification; 18 | 19 | import static android.service.notification.ZenModeConfig.ALL_DAYS; 20 | 21 | import android.content.ComponentName; 22 | import android.content.Intent; 23 | import android.content.pm.ServiceInfo; 24 | import android.content.res.Resources; 25 | import android.os.Bundle; 26 | import android.os.UserHandle; 27 | import android.preference.Preference; 28 | import android.preference.Preference.OnPreferenceClickListener; 29 | import android.preference.PreferenceScreen; 30 | import android.provider.Settings; 31 | import android.provider.Settings.Global; 32 | import android.service.notification.ConditionProviderService; 33 | import android.service.notification.ZenModeConfig; 34 | import android.service.notification.ZenModeConfig.EventInfo; 35 | import android.service.notification.ZenModeConfig.ScheduleInfo; 36 | import android.service.notification.ZenModeConfig.ZenRule; 37 | import android.text.format.DateFormat; 38 | import android.util.Log; 39 | 40 | import com.android.internal.logging.MetricsLogger; 41 | import com.android.settings.R; 42 | import com.android.settings.notification.ManagedServiceSettings.Config; 43 | import com.android.settings.notification.ZenModeEventRuleSettings.CalendarInfo; 44 | import com.android.settings.notification.ZenRuleNameDialog.RuleInfo; 45 | 46 | import java.text.SimpleDateFormat; 47 | import java.util.Arrays; 48 | import java.util.Calendar; 49 | import java.util.Comparator; 50 | import java.util.List; 51 | import java.util.TreeSet; 52 | 53 | public class ZenModeAutomationSettings extends ZenModeSettingsBase { 54 | 55 | static final Config CONFIG = getConditionProviderConfig(); 56 | 57 | // per-instance to ensure we're always using the current locale 58 | private final SimpleDateFormat mDayFormat = new SimpleDateFormat("EEE"); 59 | private final Calendar mCalendar = Calendar.getInstance(); 60 | 61 | private ServiceListing mServiceListing; 62 | 63 | @Override 64 | public void onCreate(Bundle icicle) { 65 | super.onCreate(icicle); 66 | addPreferencesFromResource(R.xml.zen_mode_automation_settings); 67 | mServiceListing = new ServiceListing(mContext, CONFIG); 68 | mServiceListing.addCallback(mServiceListingCallback); 69 | mServiceListing.reload(); 70 | mServiceListing.setListening(true); 71 | } 72 | 73 | @Override 74 | public void onDestroy() { 75 | super.onDestroy(); 76 | mServiceListing.setListening(false); 77 | mServiceListing.removeCallback(mServiceListingCallback); 78 | } 79 | 80 | @Override 81 | protected void onZenModeChanged() { 82 | // don't care 83 | } 84 | 85 | @Override 86 | protected void onZenModeConfigChanged() { 87 | updateControls(); 88 | } 89 | 90 | @Override 91 | public void onResume() { 92 | super.onResume(); 93 | updateControls(); 94 | } 95 | 96 | private void showAddRuleDialog() { 97 | new ZenRuleNameDialog(mContext, mServiceListing, null, mConfig.getAutomaticRuleNames()) { 98 | @Override 99 | public void onOk(String ruleName, RuleInfo ri) { 100 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ADD_RULE_OK); 101 | final ZenRule rule = new ZenRule(); 102 | rule.name = ruleName; 103 | rule.enabled = true; 104 | rule.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; 105 | rule.conditionId = ri.defaultConditionId; 106 | rule.component = ri.serviceComponent; 107 | final ZenModeConfig newConfig = mConfig.copy(); 108 | final String ruleId = newConfig.newRuleId(); 109 | newConfig.automaticRules.put(ruleId, rule); 110 | if (setZenModeConfig(newConfig)) { 111 | showRule(ri.settingsAction, ri.configurationActivity, ruleId, rule.name); 112 | } 113 | } 114 | }.show(); 115 | } 116 | 117 | private void showRule(String settingsAction, ComponentName configurationActivity, 118 | String ruleId, String ruleName) { 119 | if (DEBUG) Log.d(TAG, "showRule " + ruleId + " name=" + ruleName); 120 | mContext.startActivity(new Intent(settingsAction) 121 | .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 122 | .putExtra(ZenModeRuleSettingsBase.EXTRA_RULE_ID, ruleId)); 123 | } 124 | 125 | private ZenRuleInfo[] sortedRules() { 126 | final ZenRuleInfo[] rt = new ZenRuleInfo[mConfig.automaticRules.size()]; 127 | for (int i = 0; i < rt.length; i++) { 128 | final ZenRuleInfo zri = new ZenRuleInfo(); 129 | zri.id = mConfig.automaticRules.keyAt(i); 130 | zri.rule = mConfig.automaticRules.valueAt(i); 131 | rt[i] = zri; 132 | } 133 | Arrays.sort(rt, RULE_COMPARATOR); 134 | return rt; 135 | } 136 | 137 | private void updateControls() { 138 | final PreferenceScreen root = getPreferenceScreen(); 139 | root.removeAll(); 140 | if (mConfig == null) return; 141 | final ZenRuleInfo[] sortedRules = sortedRules(); 142 | for (int i = 0; i < sortedRules.length; i++) { 143 | final String id = sortedRules[i].id; 144 | final ZenRule rule = sortedRules[i].rule; 145 | final boolean isSchedule = ZenModeConfig.isValidScheduleConditionId(rule.conditionId); 146 | final boolean isEvent = ZenModeConfig.isValidEventConditionId(rule.conditionId); 147 | final Preference p = new Preference(mContext); 148 | p.setIcon(isSchedule ? R.drawable.ic_schedule 149 | : isEvent ? R.drawable.ic_event 150 | : R.drawable.ic_label); 151 | p.setTitle(rule.name); 152 | p.setSummary(computeRuleSummary(rule)); 153 | p.setPersistent(false); 154 | p.setOnPreferenceClickListener(new OnPreferenceClickListener() { 155 | @Override 156 | public boolean onPreferenceClick(Preference preference) { 157 | final String action = isSchedule ? ZenModeScheduleRuleSettings.ACTION 158 | : isEvent ? ZenModeEventRuleSettings.ACTION 159 | : ZenModeExternalRuleSettings.ACTION; 160 | showRule(action, null, id, rule.name); 161 | return true; 162 | } 163 | }); 164 | root.addPreference(p); 165 | } 166 | final Preference p = new Preference(mContext); 167 | p.setIcon(R.drawable.ic_add); 168 | p.setTitle(R.string.zen_mode_add_rule); 169 | p.setPersistent(false); 170 | p.setOnPreferenceClickListener(new OnPreferenceClickListener() { 171 | @Override 172 | public boolean onPreferenceClick(Preference preference) { 173 | MetricsLogger.action(mContext, MetricsLogger.ACTION_ZEN_ADD_RULE); 174 | showAddRuleDialog(); 175 | return true; 176 | } 177 | }); 178 | root.addPreference(p); 179 | } 180 | 181 | @Override 182 | protected int getMetricsCategory() { 183 | return MetricsLogger.NOTIFICATION_ZEN_MODE_AUTOMATION; 184 | } 185 | 186 | private String computeRuleSummary(ZenRule rule) { 187 | if (rule == null || !rule.enabled) return getString(R.string.switch_off_text); 188 | final String mode = computeZenModeCaption(getResources(), rule.zenMode); 189 | String summary = getString(R.string.switch_on_text); 190 | final ScheduleInfo schedule = ZenModeConfig.tryParseScheduleConditionId(rule.conditionId); 191 | final EventInfo event = ZenModeConfig.tryParseEventConditionId(rule.conditionId); 192 | if (schedule != null) { 193 | summary = computeScheduleRuleSummary(schedule); 194 | } else if (event != null) { 195 | summary = computeEventRuleSummary(event); 196 | } 197 | return getString(R.string.zen_mode_rule_summary_combination, summary, mode); 198 | } 199 | 200 | private String computeScheduleRuleSummary(ScheduleInfo schedule) { 201 | final String days = computeContiguousDayRanges(schedule.days); 202 | final String start = getTime(schedule.startHour, schedule.startMinute); 203 | final String end = getTime(schedule.endHour, schedule.endMinute); 204 | final String time = getString(R.string.summary_range_verbal_combination, start, end); 205 | return getString(R.string.zen_mode_rule_summary_combination, days, time); 206 | } 207 | 208 | private String computeEventRuleSummary(EventInfo event) { 209 | final String calendar = getString(R.string.zen_mode_event_rule_summary_calendar_template, 210 | computeCalendarName(event)); 211 | final String reply = getString(R.string.zen_mode_event_rule_summary_reply_template, 212 | getString(computeReply(event))); 213 | return getString(R.string.zen_mode_rule_summary_combination, calendar, reply); 214 | } 215 | 216 | private String computeCalendarName(EventInfo event) { 217 | return event.calendar != null ? event.calendar 218 | : getString(R.string.zen_mode_event_rule_summary_any_calendar); 219 | } 220 | 221 | private int computeReply(EventInfo event) { 222 | switch (event.reply) { 223 | case EventInfo.REPLY_ANY_EXCEPT_NO: 224 | return R.string.zen_mode_event_rule_reply_any_except_no; 225 | case EventInfo.REPLY_YES: 226 | return R.string.zen_mode_event_rule_reply_yes; 227 | case EventInfo.REPLY_YES_OR_MAYBE: 228 | return R.string.zen_mode_event_rule_reply_yes_or_maybe; 229 | default: 230 | throw new IllegalArgumentException("Bad reply: " + event.reply); 231 | } 232 | } 233 | 234 | private String getTime(int hour, int minute) { 235 | mCalendar.set(Calendar.HOUR_OF_DAY, hour); 236 | mCalendar.set(Calendar.MINUTE, minute); 237 | return DateFormat.getTimeFormat(mContext).format(mCalendar.getTime()); 238 | } 239 | 240 | private String computeContiguousDayRanges(int[] days) { 241 | final TreeSet daySet = new TreeSet<>(); 242 | for (int i = 0; days != null && i < days.length; i++) { 243 | daySet.add(days[i]); 244 | } 245 | if (daySet.isEmpty()) { 246 | return getString(R.string.zen_mode_schedule_rule_days_none); 247 | } 248 | final int N = ALL_DAYS.length; 249 | if (daySet.size() == N) { 250 | return getString(R.string.zen_mode_schedule_rule_days_all); 251 | } 252 | String rt = null; 253 | for (int i = 0; i < N; i++) { 254 | final int startDay = ALL_DAYS[i]; 255 | final boolean active = daySet.contains(startDay); 256 | if (!active) continue; 257 | int end = 0; 258 | while (daySet.contains(ALL_DAYS[(i + end + 1) % N])) { 259 | end++; 260 | } 261 | if (!(i == 0 && daySet.contains(ALL_DAYS[N - 1]))) { 262 | final String v = end == 0 ? dayString(startDay) 263 | : getString(R.string.summary_range_symbol_combination, 264 | dayString(startDay), 265 | dayString(ALL_DAYS[(i + end) % N])); 266 | rt = rt == null ? v : getString(R.string.summary_divider_text, rt, v); 267 | } 268 | i += end; 269 | } 270 | return rt; 271 | } 272 | 273 | private String dayString(int day) { 274 | mCalendar.set(Calendar.DAY_OF_WEEK, day); 275 | return mDayFormat.format(mCalendar.getTime()); 276 | } 277 | 278 | private static Config getConditionProviderConfig() { 279 | final Config c = new Config(); 280 | c.tag = TAG; 281 | c.setting = Settings.Secure.ENABLED_CONDITION_PROVIDERS; 282 | c.intentAction = ConditionProviderService.SERVICE_INTERFACE; 283 | c.permission = android.Manifest.permission.BIND_CONDITION_PROVIDER_SERVICE; 284 | c.noun = "condition provider"; 285 | return c; 286 | } 287 | 288 | private static String computeZenModeCaption(Resources res, int zenMode) { 289 | switch (zenMode) { 290 | case Global.ZEN_MODE_ALARMS: 291 | return res.getString(R.string.zen_mode_option_alarms); 292 | case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: 293 | return res.getString(R.string.zen_mode_option_important_interruptions); 294 | case Global.ZEN_MODE_NO_INTERRUPTIONS: 295 | return res.getString(R.string.zen_mode_option_no_interruptions); 296 | default: 297 | return null; 298 | } 299 | } 300 | 301 | private final ServiceListing.Callback mServiceListingCallback = new ServiceListing.Callback() { 302 | @Override 303 | public void onServicesReloaded(List services) { 304 | for (ServiceInfo service : services) { 305 | final RuleInfo ri = ZenModeExternalRuleSettings.getRuleInfo(service); 306 | if (ri != null && ri.serviceComponent != null 307 | && ri.settingsAction == ZenModeExternalRuleSettings.ACTION) { 308 | if (!mServiceListing.isEnabled(ri.serviceComponent)) { 309 | Log.i(TAG, "Enabling external condition provider: " + ri.serviceComponent); 310 | mServiceListing.setEnabled(ri.serviceComponent, true); 311 | } 312 | } 313 | } 314 | } 315 | }; 316 | 317 | private static final Comparator RULE_COMPARATOR = new Comparator() { 318 | @Override 319 | public int compare(ZenRuleInfo lhs, ZenRuleInfo rhs) { 320 | return key(lhs).compareTo(key(rhs)); 321 | } 322 | 323 | private String key(ZenRuleInfo zri) { 324 | final ZenRule rule = zri.rule; 325 | final int type = ZenModeConfig.isValidScheduleConditionId(rule.conditionId) ? 1 326 | : ZenModeConfig.isValidEventConditionId(rule.conditionId) ? 2 327 | : 3; 328 | return type + rule.name; 329 | } 330 | }; 331 | 332 | private static class ZenRuleInfo { 333 | String id; 334 | ZenRule rule; 335 | } 336 | 337 | } 338 | --------------------------------------------------------------------------------