diff options
author | Matt Garnes <matt@cyngn.com> | 2015-07-20 15:28:17 -0700 |
---|---|---|
committer | Matt Garnes <matt@cyngn.com> | 2015-08-06 14:52:57 -0700 |
commit | 9e6ec9e8ba2b880d0d005a01090aa6ca3ca3e465 (patch) | |
tree | 5ec99d6fb0559377e09f8c7a4a2a8c0e1f413dd1 | |
parent | 0cdb1d513c70a794db0e29696ef620c573aa96ea (diff) | |
download | vendor_cmsdk-9e6ec9e8ba2b880d0d005a01090aa6ca3ca3e465.zip vendor_cmsdk-9e6ec9e8ba2b880d0d005a01090aa6ca3ca3e465.tar.gz vendor_cmsdk-9e6ec9e8ba2b880d0d005a01090aa6ca3ca3e465.tar.bz2 |
Add AlarmClock support for CM DeskClock manipulation.
- In order to externalize the AlarmClock provider within DeskClock, move
the database contract, ClockContract, into the SDK so that interested parties can
reference it.
- Add CyanogenModAlarmClock to add new utilities for turning existing
alarms on/off and creating new alarms.
Change-Id: I1f11ccc3988bdef10d721e2038b2c7d69a4ae598
-rw-r--r-- | Android.mk | 4 | ||||
-rw-r--r-- | src/java/cyanogenmod/alarmclock/ClockContract.java | 309 | ||||
-rw-r--r-- | src/java/cyanogenmod/alarmclock/CyanogenModAlarmClock.java | 163 | ||||
-rw-r--r-- | tests/AndroidManifest.xml | 10 | ||||
-rw-r--r-- | tests/res/values/strings.xml | 1 | ||||
-rw-r--r-- | tests/src/org/cyanogenmod/tests/alarmclock/CMAlarmClockTest.java | 135 |
6 files changed, 620 insertions, 2 deletions
@@ -155,7 +155,7 @@ LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:= build/tools/droiddoc/templates-sdk LOCAL_DROIDDOC_OPTIONS:= \ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/cmsdk_stubs_current_intermediates/src \ - -stubpackages cyanogenmod.app:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.platform:org.cyanogenmod.platform \ + -stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.platform:org.cyanogenmod.platform \ -api $(INTERNAL_CM_PLATFORM_API_FILE) \ -removedApi $(INTERNAL_CM_PLATFORM_REMOVED_API_FILE) \ -nodocs \ @@ -184,7 +184,7 @@ LOCAL_MODULE := cm-system-api-stubs LOCAL_DROIDDOC_OPTIONS:=\ -stubs $(TARGET_OUT_COMMON_INTERMEDIATES)/JAVA_LIBRARIES/cmsdk_system_stubs_current_intermediates/src \ - -stubpackages cyanogenmod.app:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.platform:org.cyanogenmod.platform \ + -stubpackages cyanogenmod.alarmclock:cyanogenmod.app:cyanogenmod.os:cyanogenmod.profiles:cyanogenmod.platform:org.cyanogenmod.platform \ -showAnnotation android.annotation.SystemApi \ -api $(INTERNAL_CM_PLATFORM_SYSTEM_API_FILE) \ -removedApi $(INTERNAL_CM_PLATFORM_SYSTEM_REMOVED_API_FILE) \ diff --git a/src/java/cyanogenmod/alarmclock/ClockContract.java b/src/java/cyanogenmod/alarmclock/ClockContract.java new file mode 100644 index 0000000..66ba4af --- /dev/null +++ b/src/java/cyanogenmod/alarmclock/ClockContract.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cyanogenmod.alarmclock; + +import android.net.Uri; +import android.provider.BaseColumns; + +/** + * <p> + * The contract between the clock provider and desk clock. Contains + * definitions for the supported URIs and data columns. + * </p> + * <h3>Overview</h3> + * <p> + * ClockContract defines the data model of clock related information. + * This data is stored in a number of tables: + * </p> + * <ul> + * <li>The {@link AlarmsColumns} table holds the user created alarms</li> + * <li>The {@link InstancesColumns} table holds the current state of each + * alarm in the AlarmsColumn table. + * </li> + * <li>The {@link CitiesColumns} table holds all user selectable cities</li> + * </ul> + * + * <p> + * Requires {@link cyanogenmod.alarmclock.CyanogenModAlarmClock#READ_ALARMS_PERMISSION} + * to read from the provider. + * Requires {@link cyanogenmod.alarmclock.CyanogenModAlarmClock#WRITE_ALARMS_PERMISSION} to write + * to the provider. + * </p> + */ +public final class ClockContract { + /** + * This authority is used for writing to or querying from the clock + * provider. + */ + public static final String AUTHORITY = "com.android.deskclock"; + + /** + * This utility class cannot be instantiated + */ + private ClockContract() {} + + /** + * Constants for tables with AlarmSettings. + */ + public interface AlarmSettingColumns extends BaseColumns { + /** + * This string is used to indicate no ringtone. + */ + public static final Uri NO_RINGTONE_URI = Uri.EMPTY; + + /** + * This string is used to indicate no ringtone. + */ + public static final String NO_RINGTONE = NO_RINGTONE_URI.toString(); + + /** + * True if alarm should vibrate + * <p>Type: BOOLEAN</p> + */ + public static final String VIBRATE = "vibrate"; + + /** + * Alarm label. + * + * <p>Type: STRING</p> + */ + public static final String LABEL = "label"; + + /** + * Audio alert to play when alarm triggers. Null entry + * means use system default and entry that equal + * Uri.EMPTY.toString() means no ringtone. + * + * <p>Type: STRING</p> + */ + public static final String RINGTONE = "ringtone"; + + /** + * True if alarm should start off quiet and slowly increase volume + * <P>Type: BOOLEAN</P> + */ + public static final String INCREASING_VOLUME = "incvol"; + + /** + * Profile to change to when alarm triggers + * <P>Type: STRING</P> + */ + public static final String PROFILE = "profile"; + } + + /** + * Constants for the Alarms table, which contains the user created alarms. + */ + public interface AlarmsColumns extends AlarmSettingColumns, BaseColumns { + /** + * The content:// style URL for this table. + */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/alarms"); + + /** + * Hour in 24-hour localtime 0 - 23. + * <p>Type: INTEGER</p> + */ + public static final String HOUR = "hour"; + + /** + * Minutes in localtime 0 - 59. + * <p>Type: INTEGER</p> + */ + public static final String MINUTES = "minutes"; + + /** + * Days of the week encoded as a bit set. + * <p>Type: INTEGER</p> + * + */ + public static final String DAYS_OF_WEEK = "daysofweek"; + + /** + * True if alarm is active. + * <p>Type: BOOLEAN</p> + */ + public static final String ENABLED = "enabled"; + + /** + * Determine if alarm is deleted after it has been used. + * <p>Type: INTEGER</p> + */ + public static final String DELETE_AFTER_USE = "delete_after_use"; + } + + /** + * Constants for the Instance table, which contains the state of each alarm. + */ + public interface InstancesColumns extends AlarmSettingColumns, BaseColumns { + /** + * The content:// style URL for this table. + */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/instances"); + + + /** + * Alarm state for rtc power off alarm + */ + public static final int POWER_OFF_ALARM_STATE = -1; + + /** + * Alarm state when to show no notification. + * + * Can transitions to: + * LOW_NOTIFICATION_STATE + */ + public static final int SILENT_STATE = 0; + + /** + * Alarm state to show low priority alarm notification. + * + * Can transitions to: + * HIDE_NOTIFICATION_STATE + * HIGH_NOTIFICATION_STATE + * DISMISSED_STATE + */ + public static final int LOW_NOTIFICATION_STATE = 1; + + /** + * Alarm state to hide low priority alarm notification. + * + * Can transitions to: + * HIGH_NOTIFICATION_STATE + */ + public static final int HIDE_NOTIFICATION_STATE = 2; + + /** + * Alarm state to show high priority alarm notification. + * + * Can transitions to: + * DISMISSED_STATE + * FIRED_STATE + */ + public static final int HIGH_NOTIFICATION_STATE = 3; + + /** + * Alarm state when alarm is in snooze. + * + * Can transitions to: + * DISMISSED_STATE + * FIRED_STATE + */ + public static final int SNOOZE_STATE = 4; + + /** + * Alarm state when alarm is being fired. + * + * Can transitions to: + * DISMISSED_STATE + * SNOOZED_STATE + * MISSED_STATE + */ + public static final int FIRED_STATE = 5; + + /** + * Alarm state when alarm has been missed. + * + * Can transitions to: + * DISMISSED_STATE + */ + public static final int MISSED_STATE = 6; + + /** + * Alarm state when alarm is done. + */ + public static final int DISMISSED_STATE = 7; + + /** + * Alarm year. + * + * <p>Type: INTEGER</p> + */ + public static final String YEAR = "year"; + + /** + * Alarm month in year. + * + * <p>Type: INTEGER</p> + */ + public static final String MONTH = "month"; + + /** + * Alarm day in month. + * + * <p>Type: INTEGER</p> + */ + public static final String DAY = "day"; + + /** + * Alarm hour in 24-hour localtime 0 - 23. + * <p>Type: INTEGER</p> + */ + public static final String HOUR = "hour"; + + /** + * Alarm minutes in localtime 0 - 59 + * <p>Type: INTEGER</p> + */ + public static final String MINUTES = "minutes"; + + /** + * Foreign key to Alarms table + * <p>Type: INTEGER (long)</p> + */ + public static final String ALARM_ID = "alarm_id"; + + /** + * Alarm state + * <p>Type: INTEGER</p> + */ + public static final String ALARM_STATE = "alarm_state"; + } + + /** + * Constants for the Cities table, which contains all selectable cities. + */ + public interface CitiesColumns { + /** + * The content:// style URL for this table. + */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/cities"); + + /** + * Primary id for city. + * <p>Type: STRING</p> + */ + public static final String CITY_ID = "city_id"; + + /** + * City name. + * <p>Type: STRING</p> + */ + public static final String CITY_NAME = "city_name"; + + /** + * Timezone name of city. + * <p>Type: STRING</p> + */ + public static final String TIMEZONE_NAME = "timezone_name"; + + /** + * Timezone offset. + * <p>Type: INTEGER</p> + */ + public static final String TIMEZONE_OFFSET = "timezone_offset"; + } +} diff --git a/src/java/cyanogenmod/alarmclock/CyanogenModAlarmClock.java b/src/java/cyanogenmod/alarmclock/CyanogenModAlarmClock.java new file mode 100644 index 0000000..76508a9 --- /dev/null +++ b/src/java/cyanogenmod/alarmclock/CyanogenModAlarmClock.java @@ -0,0 +1,163 @@ +/** + * Copyright (c) 2015, The CyanogenMod Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package cyanogenmod.alarmclock; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.provider.AlarmClock; + +import java.util.List; + +/** + * The CyanogenModAlarmClock class contains utilities for interacting with + * a variety of Alarm features that the CyanogenMod AlarmClock application + * (based on AOSP DeskClock) supports. + */ +public class CyanogenModAlarmClock { + /** + * The package name of the CyanogenMod DeskClock application. + */ + private static final String DESKCLOCK_PACKAGE = "com.android.deskclock"; + + /** + * Allows an application to make modifications to existing alarms, + * such as turning them on or off. + * + * @see #ACTION_SET_ALARM_ENABLED + */ + public static final String MODIFY_ALARMS_PERMISSION + = "cyanogenmod.alarmclock.permission.MODIFY_ALARMS"; + + /** + * Allows an application to have read access to all alarms in the + * CyanogenMod DeskClock application. + * + * @see cyanogenmod.alarmclock.ClockContract + */ + public static final String READ_ALARMS_PERMISSION + = "cyanogenmod.alarmclock.permission.READ_ALARMS"; + + /** + * Allows an application to have write access to all alarms in the + * CyanogenMod DeskClock application. This is a system level permission. + * + * @see cyanogenmod.alarmclock.ClockContract + * @hide + */ + public static final String WRITE_ALARMS_PERMISSION + = "cyanogenmod.alarmclock.permission.WRITE_ALARMS"; + + /** + * Service Action: Set an existing alarm to be either enabled or disabled. + * <p> + * This action sets an alarm to be enabled or disabled. + * </p><p> + * This action requests an alarm with the id specified by {@link #EXTRA_ALARM_ID} + * be set to enabled or disabled, depending on the value set with {@link #EXTRA_ENABLED}. + * </p> + * + * <p>Requires permission {@link #MODIFY_ALARMS_PERMISSION} to launch this + * intent. + * </p> + * + * <p>Always set the package name of the Intent that will launch this action + * to {@link #DESKCLOCK_PACKAGE} explicitly, for security.</p> + * + * <h3>Request parameters</h3> + * <ul> + * <li>{@link #EXTRA_ALARM_ID} <em>(required)</em>: The id of the alarm to modify, + * as stored in {@link cyanogenmod.alarmclock.ClockContract.AlarmsColumns#_ID}</li> + * <li>{@link #EXTRA_ENABLED} <em>(required)</em>: Whether to set this alarm to be enabled + * or disabled. </li> + * </ul> + */ + public static final String ACTION_SET_ALARM_ENABLED + = "cyanogenmod.alarmclock.SET_ALARM_ENABLED"; + + /** + * Bundle extra: The id of the alarm. + * <p> + * Used by {@link #ACTION_SET_ALARM_ENABLED}. + * </p><p> + * This extra is required. + * </p><p> + * The value is an {@link Long} and is the ID stored in + * {@link cyanogenmod.alarmclock.ClockContract.AlarmsColumns#_ID} for this alarm. + * </p> + * + * @see #ACTION_SET_ALARM_ENABLED + * @see #EXTRA_ENABLED + */ + public static final String EXTRA_ALARM_ID = "cyanogenmod.intent.extra.alarmclock.ID"; + + /** + * Bundle extra: Whether to set the alarm to enabled to disabled. + * <p> + * Used by {@link #ACTION_SET_ALARM_ENABLED}. + * </p><p> + * This extra is required. + * </p><p> + * The value is an {@link Boolean} and if true, will set the alarm specified by + * {@link #EXTRA_ALARM_ID} to be enabled. Otherwise, the alarm will be disabled. + * </p> + * + * @see #ACTION_SET_ALARM_ENABLED + * @see #EXTRA_ALARM_ID + */ + public static final String EXTRA_ENABLED = "cyanogenmod.intent.extra.alarmclock.ENABLED"; + + /** + * <p> + * Retrieves an Intent that is prepopulated with the proper action and ComponentName to + * create a new alarm in the CyanogenMod DeskClock application. + * </p> + * <p> The action will be set to {@link android.provider.AlarmClock#ACTION_SET_ALARM}. Use the + * Intent extras contained at {@link android.provider.AlarmClock} to configure the alarm. + * </p> + * <p>Requires permission {@link android.Manifest.permission#SET_ALARM} to launch this + * intent. + * </p> + * + * @see android.provider.AlarmClock#ACTION_SET_ALARM + * @return The Intent to create a new alarm with the CyanogenMod DeskClock application. + */ + public static Intent createAlarmIntent(Context context) { + Intent intent = new Intent(); + intent.setAction(AlarmClock.ACTION_SET_ALARM); + + // Retrieve the ComponentName of the best result + // for ACTION_SET_ALARM within system applications only. + // This will exclude third party alarm apps that have been installed. + PackageManager pm = context.getPackageManager(); + List<ResolveInfo> resolves = pm.queryIntentActivities(intent, 0); + ComponentName selectedSystemComponent = null; + for (ResolveInfo info : resolves) { + if ((info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + selectedSystemComponent = new ComponentName(info.activityInfo.packageName, + info.activityInfo.name); + break; + } + } + if (selectedSystemComponent != null) { + intent.setComponent(selectedSystemComponent); + } + return intent; + } +} diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml index 8aae204..e4ad867 100644 --- a/tests/AndroidManifest.xml +++ b/tests/AndroidManifest.xml @@ -8,6 +8,9 @@ <uses-permission android:name="android.permission.WRITE_SETTINGS"/> <uses-permission android:name="cyanogenmod.permission.MODIFY_NETWORK_SETTINGS" /> <uses-permission android:name="android.permission.REBOOT" /> + <uses-permission android:name="com.android.alarm.permission.SET_ALARM" /> + <uses-permission android:name="cyanogenmod.alarmclock.permission.MANAGE_ALARMS" /> + <uses-permission android:name="cyanogenmod.alarmclock.permission.READ_ALARMS" /> <application android:label="@string/app_name" android:icon="@drawable/ic_launcher"> <uses-library android:name="android.test.runner" /> @@ -25,6 +28,13 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> + <activity android:name=".alarmclock.CMAlarmClockTest" + android:label="@string/alarm_tests_activity_name"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> <activity android:name=".profiles.ProfileTest" android:label="@string/app_name"> <intent-filter> diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml index e07e6c1..6195f2b 100644 --- a/tests/res/values/strings.xml +++ b/tests/res/values/strings.xml @@ -2,4 +2,5 @@ <resources> <string name="app_name">CM Platform Tests</string> <string name="settings_tests_activity_name">CM Platform Settings Tests</string> + <string name="alarm_tests_activity_name">CM Platform Alarm Clock Tests</string> </resources> diff --git a/tests/src/org/cyanogenmod/tests/alarmclock/CMAlarmClockTest.java b/tests/src/org/cyanogenmod/tests/alarmclock/CMAlarmClockTest.java new file mode 100644 index 0000000..83a1c1f --- /dev/null +++ b/tests/src/org/cyanogenmod/tests/alarmclock/CMAlarmClockTest.java @@ -0,0 +1,135 @@ +package org.cyanogenmod.tests.alarmclock; + +import android.content.Intent; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.net.Uri; +import android.provider.AlarmClock; +import android.util.Log; +import android.widget.Toast; +import cyanogenmod.alarmclock.ClockContract; +import cyanogenmod.alarmclock.CyanogenModAlarmClock; +import org.cyanogenmod.tests.TestActivity; + +/** + * Tests functionality added in {@link cyanogenmod.alarmclock.CyanogenModAlarmClock} + */ +public class CMAlarmClockTest extends TestActivity { + private static final String TAG = "CMAlarmClockTest"; + + private static final String[] ALARM_QUERY_COLUMNS = { + ClockContract.AlarmsColumns._ID, + ClockContract.AlarmsColumns.LABEL, + ClockContract.AlarmsColumns.VIBRATE, + ClockContract.AlarmsColumns.RINGTONE, + ClockContract.AlarmsColumns.INCREASING_VOLUME, + ClockContract.AlarmsColumns.PROFILE, + ClockContract.AlarmsColumns.ENABLED + }; + + @Override + protected String tag() { + return null; + } + + @Override + protected Test[] tests() { + return mTests; + } + + private Test[] mTests = new Test[] { + new Test("Test query alarms and dump to log") { + public void run() { + Uri clockUri = ClockContract.AlarmsColumns.CONTENT_URI; + Cursor allAlarms = getContentResolver().query(clockUri, + ALARM_QUERY_COLUMNS, null, null, null); + Log.d(TAG, "All alarms: " + DatabaseUtils.dumpCursorToString(allAlarms)); + if (allAlarms != null && !allAlarms.isClosed()) { + allAlarms.close(); + } + } + }, + new Test("Test create alarm") { + public void run() { + Intent intent = CyanogenModAlarmClock.createAlarmIntent(CMAlarmClockTest.this); + intent.putExtra(AlarmClock.EXTRA_HOUR, 13); + intent.putExtra(AlarmClock.EXTRA_MINUTES, 35); + intent.putExtra(AlarmClock.EXTRA_MESSAGE, "Test from third party!"); + intent.putExtra(AlarmClock.EXTRA_SKIP_UI, true); + startActivityForResult(intent, 0); + } + }, + new Test("Enable the first alarm if it exists") { + public void run() { + setAlarmEnabledAtIndex(0, true); + } + }, + new Test("Disable the first alarm if it exists") { + public void run() { + setAlarmEnabledAtIndex(0, false); + } + }, + new Test("Enable the second alarm if it exists") { + public void run() { + setAlarmEnabledAtIndex(1, true); + } + }, + new Test("Disable the second alarm if it exists") { + public void run() { + setAlarmEnabledAtIndex(1, false); + } + }, + }; + + /** + * Retrieve the id of the alarm within the Alarms table at the given index. + * @param index The index of the alarm for which to retrieve the id, beginning at zero. + * @return The ID of the alarm at the given index or -1L if + * no alarm exists at that index. + */ + private long getAlarmIdAtIndex(int index) { + Uri clockUri = ClockContract.AlarmsColumns.CONTENT_URI; + Cursor allAlarms = getContentResolver().query(clockUri, + new String[]{ClockContract.AlarmsColumns._ID}, null, null, null); + long theIdToReturn = -1L; + int current = 0; + int idColumnIndex = allAlarms.getColumnIndex(ClockContract.AlarmsColumns._ID); + allAlarms.moveToFirst(); + while(!allAlarms.isAfterLast()) { + if (current == index) { + theIdToReturn = allAlarms.getLong(idColumnIndex); + break; + } + current++; + allAlarms.moveToNext(); + } + if (allAlarms != null && !allAlarms.isClosed()) { + allAlarms.close(); + } + return theIdToReturn; + } + + /** + * Construct a new Intent that will launch a DeskClock IntentService to + * set an alarm's state to enabled or disabled. + * @param alarmId The ID of the alarm that we will toggle. + * @param enabledState The new state of the alarm, whether it will be enabled or disabled. + * @return The Intent to launch that will perform this action. + */ + private Intent getIntentToSetAlarmEnabled(long alarmId, boolean enabledState) { + Intent intent = new Intent(CyanogenModAlarmClock.ACTION_SET_ALARM_ENABLED); + intent.setPackage("com.android.deskclock"); + intent.putExtra(CyanogenModAlarmClock.EXTRA_ALARM_ID, alarmId); + intent.putExtra(CyanogenModAlarmClock.EXTRA_ENABLED, enabledState); + return intent; + } + + private void setAlarmEnabledAtIndex(int index, boolean enabled) { + long firstAlarmId = getAlarmIdAtIndex(index); + if (firstAlarmId == -1L) { + Toast.makeText(this, "Alarm not found!", Toast.LENGTH_SHORT); + } else { + startService(getIntentToSetAlarmEnabled(firstAlarmId, enabled)); + } + } +} |