From 51c39ba2d7487a305d9f6fc8724d254bce687e7d Mon Sep 17 00:00:00 2001
From: Matt Garnes
Date: Wed, 29 Jul 2015 17:19:03 -0700
Subject: [PATCH] Add API to SettingsManager to set zen mode.
- Add a new permission cyanogenmod.permission.MODIFY_SOUND_SETTINGS.
- Only allowed when the caller holds
cyanogenmod.permission.MODIFY_SOUND_SETTINGS.
- Allows the user to set zen mode to the off, priority interruptions
only, or no interruptions modes.
- For each mode change, the end condition will be set to null so that
the mode remains active indefinitely.
Change-Id: Id465509a8cc8d98953bf8cbe06cacff02b9f75a0
---
api/cm_current.txt | 70 +++++++++++++++++++
.../internal/SettingsManagerService.java | 57 +++++++++++++++
cm/res/AndroidManifest.xml | 7 ++
cm/res/res/values/strings.xml | 3 +
.../cyanogenmod/app/ISettingsManager.aidl | 1 +
src/java/cyanogenmod/app/SettingsManager.java | 56 +++++++++++++++
system-api/cm_system-current.txt | 70 +++++++++++++++++++
tests/AndroidManifest.xml | 1 +
.../tests/settings/CMSettingsManagerTest.java | 17 ++++-
9 files changed, 281 insertions(+), 1 deletion(-)
diff --git a/api/cm_current.txt b/api/cm_current.txt
index 179dd37..bfb69fe 100644
--- a/api/cm_current.txt
+++ b/api/cm_current.txt
@@ -1,3 +1,69 @@
+package cyanogenmod.alarmclock {
+
+ public final class ClockContract {
+ field public static final java.lang.String AUTHORITY = "com.android.deskclock";
+ }
+
+ public static abstract interface ClockContract.AlarmSettingColumns {
+ field public static final java.lang.String INCREASING_VOLUME = "incvol";
+ field public static final java.lang.String LABEL = "label";
+ field public static final java.lang.String NO_RINGTONE;
+ field public static final android.net.Uri NO_RINGTONE_URI;
+ field public static final java.lang.String PROFILE = "profile";
+ field public static final java.lang.String RINGTONE = "ringtone";
+ field public static final java.lang.String VIBRATE = "vibrate";
+ }
+
+ public static abstract interface ClockContract.AlarmsColumns implements cyanogenmod.alarmclock.ClockContract.AlarmSettingColumns {
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String DAYS_OF_WEEK = "daysofweek";
+ field public static final java.lang.String DELETE_AFTER_USE = "delete_after_use";
+ field public static final java.lang.String ENABLED = "enabled";
+ field public static final java.lang.String HOUR = "hour";
+ field public static final java.lang.String MINUTES = "minutes";
+ }
+
+ public static abstract interface ClockContract.CitiesColumns {
+ field public static final java.lang.String CITY_ID = "city_id";
+ field public static final java.lang.String CITY_NAME = "city_name";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String TIMEZONE_NAME = "timezone_name";
+ field public static final java.lang.String TIMEZONE_OFFSET = "timezone_offset";
+ }
+
+ public static abstract interface ClockContract.InstancesColumns implements cyanogenmod.alarmclock.ClockContract.AlarmSettingColumns {
+ field public static final java.lang.String ALARM_ID = "alarm_id";
+ field public static final java.lang.String ALARM_STATE = "alarm_state";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String DAY = "day";
+ field public static final int DISMISSED_STATE = 7; // 0x7
+ field public static final int FIRED_STATE = 5; // 0x5
+ field public static final int HIDE_NOTIFICATION_STATE = 2; // 0x2
+ field public static final int HIGH_NOTIFICATION_STATE = 3; // 0x3
+ field public static final java.lang.String HOUR = "hour";
+ field public static final int LOW_NOTIFICATION_STATE = 1; // 0x1
+ field public static final java.lang.String MINUTES = "minutes";
+ field public static final int MISSED_STATE = 6; // 0x6
+ field public static final java.lang.String MONTH = "month";
+ field public static final int POWER_OFF_ALARM_STATE = -1; // 0xffffffff
+ field public static final int SILENT_STATE = 0; // 0x0
+ field public static final int SNOOZE_STATE = 4; // 0x4
+ field public static final java.lang.String YEAR = "year";
+ }
+
+ public class CyanogenModAlarmClock {
+ ctor public CyanogenModAlarmClock();
+ method public static android.content.Intent createAlarmIntent();
+ field public static final java.lang.String ACTION_SET_ALARM_ENABLED = "cyanogenmod.alarmclock.SET_ALARM_ENABLED";
+ field public static final java.lang.String EXTRA_ALARM_ID = "cyanogenmod.intent.extra.alarmclock.ID";
+ field public static final java.lang.String EXTRA_ENABLED = "cyanogenmod.intent.extra.alarmclock.ENABLED";
+ field public static final java.lang.String MODIFY_ALARMS_PERMISSION = "cyanogenmod.alarmclock.permission.MODIFY_ALARMS";
+ field public static final java.lang.String READ_ALARMS_PERMISSION = "cyanogenmod.alarmclock.permission.READ_ALARMS";
+ field public static final java.lang.String WRITE_ALARMS_PERMISSION = "cyanogenmod.alarmclock.permission.WRITE_ALARMS";
+ }
+
+}
+
package cyanogenmod.app {
public class CMStatusBarManager {
@@ -241,7 +307,11 @@ package cyanogenmod.app {
method public void rebootDevice();
method public void setAirplaneModeEnabled(boolean);
method public void setMobileDataEnabled(boolean);
+ method public boolean setZenMode(int);
method public void shutdownDevice();
+ field public static final int ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; // 0x1
+ field public static final int ZEN_MODE_NO_INTERRUPTIONS = 2; // 0x2
+ field public static final int ZEN_MODE_OFF = 0; // 0x0
}
public class StatusBarPanelCustomTile implements android.os.Parcelable {
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/SettingsManagerService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/SettingsManagerService.java
index 2aa57b9..ae50755 100644
--- a/cm/lib/main/java/org/cyanogenmod/platform/internal/SettingsManagerService.java
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/SettingsManagerService.java
@@ -16,6 +16,8 @@
package org.cyanogenmod.platform.internal;
+import android.app.INotificationManager;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
@@ -48,6 +50,7 @@ public class SettingsManagerService extends SystemService {
private Context mContext;
private TelephonyManager mTelephonyManager;
+ private INotificationManager mNotificationManager;
public SettingsManagerService(Context context) {
super(context);
@@ -59,6 +62,8 @@ public class SettingsManagerService extends SystemService {
public void onStart() {
mTelephonyManager = (TelephonyManager)
mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mNotificationManager = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
}
private void enforceModifyNetworkSettingsPermission() {
@@ -66,6 +71,11 @@ public class SettingsManagerService extends SystemService {
"You do not have permissions to change system network settings.");
}
+ private void enforceModifySoundSettingsPermission() {
+ mContext.enforceCallingOrSelfPermission(SettingsManager.MODIFY_SOUND_SETTINGS_PERMISSION,
+ "You do not have permissions to change system sound settings.");
+ }
+
private void enforceShutdownPermission() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT,
"You do not have permissions to shut down the device.");
@@ -124,6 +134,20 @@ public class SettingsManagerService extends SystemService {
shutdownInternal(true);
restoreCallingIdentity(token);
}
+
+ @Override
+ public boolean setZenMode(int mode) {
+ enforceModifySoundSettingsPermission();
+ /*
+ * We need to clear the caller's identity in order to
+ * allow this method call to modify settings
+ * not allowed by the caller's permissions.
+ */
+ long token = clearCallingIdentity();
+ boolean success = setZenModeInternal(mode);
+ restoreCallingIdentity(token);
+ return success;
+ }
};
private void setAirplaneModeEnabledInternal(boolean enabled) {
@@ -154,5 +178,38 @@ public class SettingsManagerService extends SystemService {
Log.d(TAG, "Unable to shutdown.");
}
}
+
+ private boolean setZenModeInternal(int mode) {
+ ContentResolver contentResolver = mContext.getContentResolver();
+ int zenModeValue = -1;
+ switch(mode) {
+ case SettingsManager.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+ zenModeValue = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
+ break;
+ case SettingsManager.ZEN_MODE_OFF:
+ zenModeValue = Settings.Global.ZEN_MODE_OFF;
+ break;
+ case SettingsManager.ZEN_MODE_NO_INTERRUPTIONS:
+ zenModeValue = Settings.Global.ZEN_MODE_NO_INTERRUPTIONS;
+ break;
+ default:
+ // Invalid mode parameter
+ Log.w(TAG, "setZenMode() called with invalid mode: " + mode);
+ return false;
+ }
+ Settings.Global.putInt(contentResolver,
+ Settings.Global.ZEN_MODE,
+ zenModeValue);
+ try {
+ // Setting the exit condition to null signifies "indefinitely"
+ mNotificationManager.setZenModeCondition(null);
+ } catch (RemoteException e) {
+ // An error occurred, return false since the
+ // condition failed to set.
+ Log.e(TAG, "setZenMode() failed for mode: " + mode);
+ return false;
+ }
+ return true;
+ }
}
diff --git a/cm/res/AndroidManifest.xml b/cm/res/AndroidManifest.xml
index 003c920..7a7249d 100644
--- a/cm/res/AndroidManifest.xml
+++ b/cm/res/AndroidManifest.xml
@@ -42,6 +42,13 @@
android:icon="@drawable/ic_launcher_cyanogenmod"
android:protectionLevel="system|signature" />
+
+
+
change system network settings
Allows an app to make changes to a restricted set of system network settings.
+ change system sound settings
+ Allows an app to make changes to a restricted set of system sound settings.
+
bind to a custom tile listener service
Allows the app to bind to the top-level interface of a custom tile listener service.
diff --git a/src/java/cyanogenmod/app/ISettingsManager.aidl b/src/java/cyanogenmod/app/ISettingsManager.aidl
index 83d7bf0..86fa36c 100644
--- a/src/java/cyanogenmod/app/ISettingsManager.aidl
+++ b/src/java/cyanogenmod/app/ISettingsManager.aidl
@@ -22,6 +22,7 @@ interface ISettingsManager
{
void setAirplaneModeEnabled(boolean enabled);
void setMobileDataEnabled(boolean enabled);
+ boolean setZenMode(int mode);
void shutdown();
void reboot();
}
diff --git a/src/java/cyanogenmod/app/SettingsManager.java b/src/java/cyanogenmod/app/SettingsManager.java
index 2ab871b..b8a5802 100644
--- a/src/java/cyanogenmod/app/SettingsManager.java
+++ b/src/java/cyanogenmod/app/SettingsManager.java
@@ -28,6 +28,30 @@ import android.util.Log;
*
*/
public class SettingsManager {
+ /**
+ * Turn off zen mode. This restores the original ring and interruption
+ * settings that the user had set before zen mode was enabled.
+ *
+ * @see #setZenMode
+ */
+ public static final int ZEN_MODE_OFF = 0;
+ /**
+ * Sets zen mode to important interruptions mode, so that
+ * only notifications that the user has chosen in Settings
+ * to be of high importance will cause the user's device to notify them.
+ *
+ * This condition is held indefinitely until changed again.
+ *
+ * @see #setZenMode
+ */
+ public static final int ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1;
+ /**
+ * Sets zen mode so that no interruptions will be allowed. The device is
+ * effectively silenced indefinitely, until the mode is changed again.
+ *
+ * @see #setZenMode
+ */
+ public static final int ZEN_MODE_NO_INTERRUPTIONS = 2;
private static ISettingsManager sService;
@@ -40,6 +64,12 @@ public class SettingsManager {
public static final String MODIFY_NETWORK_SETTINGS_PERMISSION =
"cyanogenmod.permission.MODIFY_NETWORK_SETTINGS";
+ /**
+ * Allows an application to change system sound settings, such as the zen mode.
+ */
+ public static final String MODIFY_SOUND_SETTINGS_PERMISSION =
+ "cyanogenmod.permission.MODIFY_SOUND_SETTINGS";
+
private static final String TAG = "SettingsManager";
private static SettingsManager sSettingsManagerInstance;
@@ -116,6 +146,32 @@ public class SettingsManager {
}
}
+ /**
+ * Set the zen mode for the device.
+ *
+ * You will need {@link #MODIFY_SOUND_SETTINGS_PERMISSION}
+ * to utilize this functionality.
+ *
+ * @see #ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ * @see #ZEN_MODE_NO_INTERRUPTIONS
+ * @see #ZEN_MODE_OFF
+ * @param mode The zen mode to set the device to.
+ * One of {@link #ZEN_MODE_IMPORTANT_INTERRUPTIONS},
+ * {@link #ZEN_MODE_NO_INTERRUPTIONS} or
+ * {@link #ZEN_MODE_OFF}.
+ */
+ public boolean setZenMode(int mode) {
+ if (sService == null) {
+ return false;
+ }
+ try {
+ return getService().setZenMode(mode);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getLocalizedMessage(), e);
+ }
+ return false;
+ }
+
/**
* Shuts down the device, immediately.
*
diff --git a/system-api/cm_system-current.txt b/system-api/cm_system-current.txt
index 179dd37..bfb69fe 100644
--- a/system-api/cm_system-current.txt
+++ b/system-api/cm_system-current.txt
@@ -1,3 +1,69 @@
+package cyanogenmod.alarmclock {
+
+ public final class ClockContract {
+ field public static final java.lang.String AUTHORITY = "com.android.deskclock";
+ }
+
+ public static abstract interface ClockContract.AlarmSettingColumns {
+ field public static final java.lang.String INCREASING_VOLUME = "incvol";
+ field public static final java.lang.String LABEL = "label";
+ field public static final java.lang.String NO_RINGTONE;
+ field public static final android.net.Uri NO_RINGTONE_URI;
+ field public static final java.lang.String PROFILE = "profile";
+ field public static final java.lang.String RINGTONE = "ringtone";
+ field public static final java.lang.String VIBRATE = "vibrate";
+ }
+
+ public static abstract interface ClockContract.AlarmsColumns implements cyanogenmod.alarmclock.ClockContract.AlarmSettingColumns {
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String DAYS_OF_WEEK = "daysofweek";
+ field public static final java.lang.String DELETE_AFTER_USE = "delete_after_use";
+ field public static final java.lang.String ENABLED = "enabled";
+ field public static final java.lang.String HOUR = "hour";
+ field public static final java.lang.String MINUTES = "minutes";
+ }
+
+ public static abstract interface ClockContract.CitiesColumns {
+ field public static final java.lang.String CITY_ID = "city_id";
+ field public static final java.lang.String CITY_NAME = "city_name";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String TIMEZONE_NAME = "timezone_name";
+ field public static final java.lang.String TIMEZONE_OFFSET = "timezone_offset";
+ }
+
+ public static abstract interface ClockContract.InstancesColumns implements cyanogenmod.alarmclock.ClockContract.AlarmSettingColumns {
+ field public static final java.lang.String ALARM_ID = "alarm_id";
+ field public static final java.lang.String ALARM_STATE = "alarm_state";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String DAY = "day";
+ field public static final int DISMISSED_STATE = 7; // 0x7
+ field public static final int FIRED_STATE = 5; // 0x5
+ field public static final int HIDE_NOTIFICATION_STATE = 2; // 0x2
+ field public static final int HIGH_NOTIFICATION_STATE = 3; // 0x3
+ field public static final java.lang.String HOUR = "hour";
+ field public static final int LOW_NOTIFICATION_STATE = 1; // 0x1
+ field public static final java.lang.String MINUTES = "minutes";
+ field public static final int MISSED_STATE = 6; // 0x6
+ field public static final java.lang.String MONTH = "month";
+ field public static final int POWER_OFF_ALARM_STATE = -1; // 0xffffffff
+ field public static final int SILENT_STATE = 0; // 0x0
+ field public static final int SNOOZE_STATE = 4; // 0x4
+ field public static final java.lang.String YEAR = "year";
+ }
+
+ public class CyanogenModAlarmClock {
+ ctor public CyanogenModAlarmClock();
+ method public static android.content.Intent createAlarmIntent();
+ field public static final java.lang.String ACTION_SET_ALARM_ENABLED = "cyanogenmod.alarmclock.SET_ALARM_ENABLED";
+ field public static final java.lang.String EXTRA_ALARM_ID = "cyanogenmod.intent.extra.alarmclock.ID";
+ field public static final java.lang.String EXTRA_ENABLED = "cyanogenmod.intent.extra.alarmclock.ENABLED";
+ field public static final java.lang.String MODIFY_ALARMS_PERMISSION = "cyanogenmod.alarmclock.permission.MODIFY_ALARMS";
+ field public static final java.lang.String READ_ALARMS_PERMISSION = "cyanogenmod.alarmclock.permission.READ_ALARMS";
+ field public static final java.lang.String WRITE_ALARMS_PERMISSION = "cyanogenmod.alarmclock.permission.WRITE_ALARMS";
+ }
+
+}
+
package cyanogenmod.app {
public class CMStatusBarManager {
@@ -241,7 +307,11 @@ package cyanogenmod.app {
method public void rebootDevice();
method public void setAirplaneModeEnabled(boolean);
method public void setMobileDataEnabled(boolean);
+ method public boolean setZenMode(int);
method public void shutdownDevice();
+ field public static final int ZEN_MODE_IMPORTANT_INTERRUPTIONS = 1; // 0x1
+ field public static final int ZEN_MODE_NO_INTERRUPTIONS = 2; // 0x2
+ field public static final int ZEN_MODE_OFF = 0; // 0x0
}
public class StatusBarPanelCustomTile implements android.os.Parcelable {
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index e4ad867..43f4925 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -7,6 +7,7 @@
+
diff --git a/tests/src/org/cyanogenmod/tests/settings/CMSettingsManagerTest.java b/tests/src/org/cyanogenmod/tests/settings/CMSettingsManagerTest.java
index adbf459..f0bd97f 100644
--- a/tests/src/org/cyanogenmod/tests/settings/CMSettingsManagerTest.java
+++ b/tests/src/org/cyanogenmod/tests/settings/CMSettingsManagerTest.java
@@ -49,6 +49,21 @@ public class CMSettingsManagerTest extends TestActivity {
public void run() {
mSettingsManager.shutdownDevice();
}
- }
+ },
+ new Test("Test set zen mode to important interruptions") {
+ public void run() {
+ mSettingsManager.setZenMode(SettingsManager.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
+ }
+ },
+ new Test("Test set zen mode to no interruptions") {
+ public void run() {
+ mSettingsManager.setZenMode(SettingsManager.ZEN_MODE_NO_INTERRUPTIONS);
+ }
+ },
+ new Test("Test turn zen mode off") {
+ public void run() {
+ mSettingsManager.setZenMode(SettingsManager.ZEN_MODE_OFF);
+ }
+ },
};
}