OSDN Git Service

Initial Contribution
authorThe Android Open Source Project <initial-contribution@android.com>
Tue, 21 Oct 2008 14:00:00 +0000 (07:00 -0700)
committerThe Android Open Source Project <initial-contribution@android.com>
Tue, 21 Oct 2008 14:00:00 +0000 (07:00 -0700)
59 files changed:
Android.mk [new file with mode: 0644]
AndroidManifest.xml [new file with mode: 0644]
MODULE_LICENSE_APACHE2 [new file with mode: 0644]
NOTICE [new file with mode: 0644]
res/drawable/animate_circle.xml [new file with mode: 0644]
res/drawable/app_alarm_clock.png [new file with mode: 0644]
res/drawable/background_gradient.xml [new file with mode: 0644]
res/drawable/circle0.png [new file with mode: 0644]
res/drawable/circle1.png [new file with mode: 0644]
res/drawable/circle2.png [new file with mode: 0644]
res/drawable/circle3.png [new file with mode: 0644]
res/drawable/clock_dial.png [new file with mode: 0644]
res/drawable/clock_hour.png [new file with mode: 0644]
res/drawable/clock_minute.png [new file with mode: 0644]
res/drawable/clockdroid2_dial.png [new file with mode: 0644]
res/drawable/clockdroid2_hour.png [new file with mode: 0644]
res/drawable/clockdroid2_minute.png [new file with mode: 0644]
res/drawable/clockdroids_dial.png [new file with mode: 0644]
res/drawable/clockdroids_hour.png [new file with mode: 0644]
res/drawable/clockdroids_minute.png [new file with mode: 0644]
res/drawable/clockgoog_dial.png [new file with mode: 0644]
res/drawable/clockgoog_hour.png [new file with mode: 0644]
res/drawable/clockgoog_minute.png [new file with mode: 0644]
res/drawable/ic_launcher_alarmclock.png [new file with mode: 0755]
res/drawable/ic_menu_clock_face.png [new file with mode: 0644]
res/drawable/stat_notify_alarm.png [new file with mode: 0644]
res/layout-land/alarm_alert.xml [new file with mode: 0644]
res/layout-land/alarm_clock.xml [new file with mode: 0644]
res/layout/alarm_alert.xml [new file with mode: 0644]
res/layout/alarm_clock.xml [new file with mode: 0644]
res/layout/alarm_time.xml [new file with mode: 0644]
res/layout/analog_clock.xml [new file with mode: 0644]
res/layout/clock_basic_bw.xml [new file with mode: 0644]
res/layout/clock_droid2.xml [new file with mode: 0644]
res/layout/clock_droids.xml [new file with mode: 0644]
res/layout/clock_googly.xml [new file with mode: 0644]
res/layout/clockpicker.xml [new file with mode: 0644]
res/layout/digital_clock.xml [new file with mode: 0644]
res/values-cs/strings.xml [new file with mode: 0644]
res/values-de-rDE/strings.xml [new file with mode: 0644]
res/values-en-rGB/strings.xml [new file with mode: 0644]
res/values/colors.xml [new file with mode: 0644]
res/values/strings.xml [new file with mode: 0644]
res/values/styles.xml [new file with mode: 0644]
res/xml/alarm_prefs.xml [new file with mode: 0644]
src/com/android/alarmclock/AlarmAlert.java [new file with mode: 0644]
src/com/android/alarmclock/AlarmAlertWakeLock.java [new file with mode: 0644]
src/com/android/alarmclock/AlarmClock.java [new file with mode: 0644]
src/com/android/alarmclock/AlarmInitReceiver.java [new file with mode: 0644]
src/com/android/alarmclock/AlarmPreference.java [new file with mode: 0644]
src/com/android/alarmclock/AlarmProvider.java [new file with mode: 0644]
src/com/android/alarmclock/AlarmReceiver.java [new file with mode: 0644]
src/com/android/alarmclock/Alarms.java [new file with mode: 0644]
src/com/android/alarmclock/ClockPicker.java [new file with mode: 0644]
src/com/android/alarmclock/DigitalClock.java [new file with mode: 0644]
src/com/android/alarmclock/Log.java [new file with mode: 0644]
src/com/android/alarmclock/RepeatPreference.java [new file with mode: 0644]
src/com/android/alarmclock/SetAlarm.java [new file with mode: 0644]
src/com/android/alarmclock/ToastMaster.java [new file with mode: 0644]

diff --git a/Android.mk b/Android.mk
new file mode 100644 (file)
index 0000000..3b05373
--- /dev/null
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := eng development
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := AlarmClock
+
+include $(BUILD_PACKAGE)
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644 (file)
index 0000000..2a1f108
--- /dev/null
@@ -0,0 +1,47 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.alarmclock">
+
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <application android:label="@string/app_label"
+                 android:icon="@drawable/ic_launcher_alarmclock">
+
+        <provider android:name="AlarmProvider" android:authorities="com.android.alarmclock" />
+
+        <activity android:name="AlarmClock" android:label="Alarm Clock">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="SetAlarm" android:label="Set Alarm" />
+
+        <activity android:name="AlarmAlert" android:label="Alarm"
+                android:theme="@android:style/Theme.Dialog"
+                android:launchMode="singleTask"
+                android:taskAffinity=":AlarmAlert" />
+
+        <activity android:name="ClockPicker" />
+
+        <receiver android:name="AlarmReceiver">
+            <intent-filter>
+               <action android:name="com.android.alarmclock.ALARM_ALERT" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="AlarmInitReceiver">
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+                <action android:name="android.intent.action.TIME_SET" />
+                <action android:name="android.intent.action.TIMEZONE_CHANGED" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
+
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/NOTICE b/NOTICE
new file mode 100644 (file)
index 0000000..c5b1efa
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/res/drawable/animate_circle.xml b/res/drawable/animate_circle.xml
new file mode 100644 (file)
index 0000000..02dfd51
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!--  Chewie... the hyperdrive ain't working -->
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:oneshot="false" android:visible="true">
+    <item android:drawable="@drawable/circle0" android:duration="80" />
+    <item android:drawable="@drawable/circle1" android:duration="80" />
+    <item android:drawable="@drawable/circle2" android:duration="80" />
+    <item android:drawable="@drawable/circle3" android:duration="80" />
+</animation-list>
+
+
+
diff --git a/res/drawable/app_alarm_clock.png b/res/drawable/app_alarm_clock.png
new file mode 100644 (file)
index 0000000..d91cdc8
Binary files /dev/null and b/res/drawable/app_alarm_clock.png differ
diff --git a/res/drawable/background_gradient.xml b/res/drawable/background_gradient.xml
new file mode 100644 (file)
index 0000000..d61a9c0
--- /dev/null
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <color android:color="#ff707070"/>
+    </item>
+    <item android:id="@+id/pinstripe_layer">
+        <color android:color="#00000000"/> <!-- dummy, replaced by PinstripeDrawable -->
+    </item>
+    <item>
+        <shape>
+            <!-- non-gradient gradient, until dithering works -->
+            <gradient android:startColor="#80000000" android:endColor="#80000000"
+                android:type="radial" android:centerX="0.5" android:centerY="0.5"
+                android:gradientRadius="320"/>
+        </shape>
+    </item>
+</layer-list>
diff --git a/res/drawable/circle0.png b/res/drawable/circle0.png
new file mode 100644 (file)
index 0000000..1386ed4
Binary files /dev/null and b/res/drawable/circle0.png differ
diff --git a/res/drawable/circle1.png b/res/drawable/circle1.png
new file mode 100644 (file)
index 0000000..fed0190
Binary files /dev/null and b/res/drawable/circle1.png differ
diff --git a/res/drawable/circle2.png b/res/drawable/circle2.png
new file mode 100644 (file)
index 0000000..9712976
Binary files /dev/null and b/res/drawable/circle2.png differ
diff --git a/res/drawable/circle3.png b/res/drawable/circle3.png
new file mode 100644 (file)
index 0000000..056bb45
Binary files /dev/null and b/res/drawable/circle3.png differ
diff --git a/res/drawable/clock_dial.png b/res/drawable/clock_dial.png
new file mode 100644 (file)
index 0000000..734e361
Binary files /dev/null and b/res/drawable/clock_dial.png differ
diff --git a/res/drawable/clock_hour.png b/res/drawable/clock_hour.png
new file mode 100644 (file)
index 0000000..023faa4
Binary files /dev/null and b/res/drawable/clock_hour.png differ
diff --git a/res/drawable/clock_minute.png b/res/drawable/clock_minute.png
new file mode 100644 (file)
index 0000000..104e2a2
Binary files /dev/null and b/res/drawable/clock_minute.png differ
diff --git a/res/drawable/clockdroid2_dial.png b/res/drawable/clockdroid2_dial.png
new file mode 100644 (file)
index 0000000..0996f09
Binary files /dev/null and b/res/drawable/clockdroid2_dial.png differ
diff --git a/res/drawable/clockdroid2_hour.png b/res/drawable/clockdroid2_hour.png
new file mode 100644 (file)
index 0000000..cf6268d
Binary files /dev/null and b/res/drawable/clockdroid2_hour.png differ
diff --git a/res/drawable/clockdroid2_minute.png b/res/drawable/clockdroid2_minute.png
new file mode 100644 (file)
index 0000000..a4fe782
Binary files /dev/null and b/res/drawable/clockdroid2_minute.png differ
diff --git a/res/drawable/clockdroids_dial.png b/res/drawable/clockdroids_dial.png
new file mode 100644 (file)
index 0000000..45e86d0
Binary files /dev/null and b/res/drawable/clockdroids_dial.png differ
diff --git a/res/drawable/clockdroids_hour.png b/res/drawable/clockdroids_hour.png
new file mode 100644 (file)
index 0000000..898d44d
Binary files /dev/null and b/res/drawable/clockdroids_hour.png differ
diff --git a/res/drawable/clockdroids_minute.png b/res/drawable/clockdroids_minute.png
new file mode 100644 (file)
index 0000000..4e59764
Binary files /dev/null and b/res/drawable/clockdroids_minute.png differ
diff --git a/res/drawable/clockgoog_dial.png b/res/drawable/clockgoog_dial.png
new file mode 100644 (file)
index 0000000..a83b7b8
Binary files /dev/null and b/res/drawable/clockgoog_dial.png differ
diff --git a/res/drawable/clockgoog_hour.png b/res/drawable/clockgoog_hour.png
new file mode 100644 (file)
index 0000000..7eff3c2
Binary files /dev/null and b/res/drawable/clockgoog_hour.png differ
diff --git a/res/drawable/clockgoog_minute.png b/res/drawable/clockgoog_minute.png
new file mode 100644 (file)
index 0000000..b1eaebd
Binary files /dev/null and b/res/drawable/clockgoog_minute.png differ
diff --git a/res/drawable/ic_launcher_alarmclock.png b/res/drawable/ic_launcher_alarmclock.png
new file mode 100755 (executable)
index 0000000..30ff267
Binary files /dev/null and b/res/drawable/ic_launcher_alarmclock.png differ
diff --git a/res/drawable/ic_menu_clock_face.png b/res/drawable/ic_menu_clock_face.png
new file mode 100644 (file)
index 0000000..678af24
Binary files /dev/null and b/res/drawable/ic_menu_clock_face.png differ
diff --git a/res/drawable/stat_notify_alarm.png b/res/drawable/stat_notify_alarm.png
new file mode 100644 (file)
index 0000000..1b01b85
Binary files /dev/null and b/res/drawable/stat_notify_alarm.png differ
diff --git a/res/layout-land/alarm_alert.xml b/res/layout-land/alarm_alert.xml
new file mode 100644 (file)
index 0000000..75b8a13
--- /dev/null
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root"
+    android:layout_width="fill_parent" 
+    android:layout_height="fill_parent">
+
+    <LinearLayout
+        android:id="@+id/clockView"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center"/> 
+
+    <LinearLayout
+        android:layout_width="wrap_content" 
+        android:layout_height="fill_parent"
+        android:orientation="vertical">
+
+        <Button
+            android:id="@+id/snooze"
+            android:layout_width="96dip"
+            android:layout_height="wrap_content"
+            android:text="@string/alarm_alert_snooze_text" />
+
+        <!-- blank stretchable view -->
+        <View
+            android:layout_width="2dip"
+            android:layout_gravity="fill_vertical" 
+            android:layout_weight="1" 
+            android:layout_height="2dip"/>
+
+        <Button
+            android:id="@+id/dismiss"
+            android:layout_width="96dip"
+            android:layout_height="wrap_content"
+            android:text="@string/alarm_alert_dismiss_text" />
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/layout-land/alarm_clock.xml b/res/layout-land/alarm_clock.xml
new file mode 100644 (file)
index 0000000..5b51e53
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/base_layout"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:gravity="center_vertical"> 
+
+    <FrameLayout
+        android:id="@+id/clock_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="8dip"/> 
+
+    <ListView
+        android:id="@+id/alarms_list"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
diff --git a/res/layout/alarm_alert.xml b/res/layout/alarm_alert.xml
new file mode 100644 (file)
index 0000000..0da9600
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root"
+    android:layout_width="fill_parent" 
+    android:layout_height="fill_parent"
+    android:gravity="center_horizontal"
+    android:orientation="vertical">
+
+    <LinearLayout
+        android:id="@+id/clockView"
+        android:layout_width="208dip"
+        android:layout_height="wrap_content"
+        android:gravity="center"/> 
+
+    <LinearLayout
+        android:layout_width="fill_parent" 
+        android:layout_height="wrap_content">
+
+        <Button
+            android:id="@+id/snooze"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="3"
+            android:text="@string/alarm_alert_snooze_text" />
+
+        <!-- blank stretchable view -->
+        <View
+            android:layout_width="2dip"
+            android:layout_height="2dip"
+            android:layout_gravity="fill_horizontal" 
+            android:layout_weight="2"/>
+        
+        <Button
+            android:id="@+id/dismiss"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="3"
+            android:text="@string/alarm_alert_dismiss_text" />
+
+    </LinearLayout>
+</LinearLayout>
diff --git a/res/layout/alarm_clock.xml b/res/layout/alarm_clock.xml
new file mode 100644 (file)
index 0000000..a0ca8e3
--- /dev/null
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/base_layout"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:layout_marginTop="6dip"
+    android:orientation="vertical"> 
+
+    <LinearLayout
+        android:id="@+id/clock_layout"
+        android:layout_width="fill_parent"
+        android:layout_height="208dip"
+        android:layout_marginBottom="8dip"
+        android:gravity="center"/> 
+
+    <ListView
+        android:id="@+id/alarms_list"
+        android:layout_width="fill_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1.0" />
+
+</LinearLayout>
diff --git a/res/layout/alarm_time.xml b/res/layout/alarm_time.xml
new file mode 100644 (file)
index 0000000..aae6ef2
--- /dev/null
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent" 
+    android:layout_height="wrap_content">
+
+    <!-- A layout that displays the time.  Shows time, am/pm (if 12-hour),
+         and an optional line below, used for day/days of week -->
+
+    <com.android.alarmclock.DigitalClock android:id="@+id/digitalClock"
+        android:layout_width="wrap_content"
+        android:layout_height="fill_parent"
+        android:focusable="true"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:orientation="vertical"
+        android:paddingTop="4dp"
+        android:paddingLeft="8dp"
+        android:background="@android:drawable/menuitem_background">
+
+        <LinearLayout
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:gravity="left">
+
+            <TextView android:id="@+id/timeDisplay"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:paddingTop="-4dp"
+                android:paddingBottom="-4dp"
+                android:textSize="28sp"
+                android:gravity="left"
+                android:textColor="@color/white"/>
+
+            <LinearLayout android:id="@+id/am_pm"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="4dp"
+                android:orientation="vertical">
+
+                <TextView android:id="@+id/am"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="3dp"
+                    android:text="@string/am"
+                    android:textSize="12sp"/>
+                <TextView android:id="@+id/pm"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="-4dp"
+                    android:text="@string/pm"
+                    android:textSize="12sp"/>
+            </LinearLayout>
+        </LinearLayout>
+
+        <TextView android:id="@+id/daysOfWeek"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:textSize="10sp"
+            android:textColor="@color/ltgrey"
+            android:layout_marginLeft="2dp"
+            android:gravity="left"/>
+
+    </com.android.alarmclock.DigitalClock>
+
+    <CheckBox android:id="@+id/alarmButton"
+        android:layout_width="60dp"
+        android:layout_height="60dp"
+        android:layout_gravity="center_vertical"/>
+
+</LinearLayout>
diff --git a/res/layout/analog_clock.xml b/res/layout/analog_clock.xml
new file mode 100644 (file)
index 0000000..345042c
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<AnalogClock android:id="@+id/clock"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="96dip" 
+    android:layout_gravity="center_horizontal"
+    android:layout_height="wrap_content"/>
diff --git a/res/layout/clock_basic_bw.xml b/res/layout/clock_basic_bw.xml
new file mode 100644 (file)
index 0000000..b1593e9
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<AnalogClock xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clock"
+    style="@style/analogClock"
+    android:dial="@drawable/clock_dial"
+    android:hand_hour="@drawable/clock_hour"
+    android:hand_minute="@drawable/clock_minute"/>
diff --git a/res/layout/clock_droid2.xml b/res/layout/clock_droid2.xml
new file mode 100644 (file)
index 0000000..336efd3
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<AnalogClock xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clock"
+    style="@style/analogClock"
+    android:dial="@drawable/clockdroid2_dial"
+    android:hand_hour="@drawable/clockdroid2_hour"
+    android:hand_minute="@drawable/clockdroid2_minute"/>
diff --git a/res/layout/clock_droids.xml b/res/layout/clock_droids.xml
new file mode 100644 (file)
index 0000000..9ec79b6
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<AnalogClock xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clock"
+    style="@style/analogClock"
+    android:dial="@drawable/clockdroids_dial"
+    android:hand_hour="@drawable/clockdroids_hour"
+    android:hand_minute="@drawable/clockdroids_minute"/>
diff --git a/res/layout/clock_googly.xml b/res/layout/clock_googly.xml
new file mode 100644 (file)
index 0000000..cb09d85
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<AnalogClock xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/clock"
+    style="@style/analogClock"
+    android:dial="@drawable/clockgoog_dial"
+    android:hand_hour="@drawable/clockgoog_hour"
+    android:hand_minute="@drawable/clockgoog_minute"/>
diff --git a/res/layout/clockpicker.xml b/res/layout/clockpicker.xml
new file mode 100644 (file)
index 0000000..2e2c3b9
--- /dev/null
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
+    android:layout_width="fill_parent" 
+    android:layout_height="fill_parent"> 
+    
+    <TextView
+        android:id="@+id/instructions"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="5dip"
+        android:background="#70000000"
+        android:text="@string/clock_instructions"
+        android:layout_alignParentTop="true"
+        android:layout_centerHorizontal="true"
+    />
+    <LinearLayout
+        android:id="@+id/clock_layout"
+        android:layout_width="fill_parent"
+        android:layout_below="@+id/instructions"
+        android:layout_height="208dip"
+        android:gravity="center"/> 
+    
+    <Gallery android:id="@+id/gallery"
+        android:background="#70000000"
+        android:layout_width="fill_parent"
+        android:layout_height="80dip"
+        android:layout_alignParentBottom="true"
+        android:layout_alignParentLeft="true"
+        android:gravity="center_vertical"
+        android:spacing="16dp"
+    />
+
+</RelativeLayout>
+   
diff --git a/res/layout/digital_clock.xml b/res/layout/digital_clock.xml
new file mode 100644 (file)
index 0000000..95d9f5e
--- /dev/null
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<com.android.alarmclock.DigitalClock android:id="@+id/digitalClock"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="208dip"
+    android:gravity="center">
+
+    <!-- Includes vertical padding so animated background doesn't
+         stretch much -->
+    <TextView android:id="@+id/timeDisplay"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textSize="64sp"
+        android:paddingRight="4dip"
+        android:paddingTop="48dip"
+        android:paddingBottom="48dip"
+        android:textColor="@color/white"/>
+
+    <LinearLayout android:id="@+id/am_pm"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <TextView android:id="@+id/am"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="-7dp"
+            android:text="@string/am"
+            android:textSize="28sp"/>
+        <TextView android:id="@+id/pm"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="-4dp"
+            android:text="@string/pm"
+            android:textSize="28sp"/>
+    </LinearLayout>
+</com.android.alarmclock.DigitalClock>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
new file mode 100644 (file)
index 0000000..95a44d0
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="alarm_alert_dismiss_text">Zrušit</string>
+  <string name="alarm_alert_dismissed">Připomenutí zrušeno</string>
+  <string name="alarm_alert_snooze_set">Připomenout znovu za 10 minut</string>
+  <string name="alarm_alert_snooze_text">Připomenout znovu</string>
+  <string name="alarm_set">Připomenutí nastaveno na {0} odteď.</string>
+  <string name="alert">Připomenutí: </string>
+  <string name="and">" a "</string>
+  <string name="date_format">Formát data</string>
+  <string name="date_format_12">12 hodinový</string>
+  <string name="date_format_24">24 hodinový</string>
+  <string name="hour">1 hodina</string>
+  <string name="hours">{0} hodin</string>
+  <string name="minute">"1 minuta"</string>
+  <string name="minutes">"{0} minut"</string>
+  <string name="set_date_formats_hour_format_text">Hod.</string>
+  <string name="set_time">Nastavit čas a datum</string>
+  <string name="set_timezone">Nastavit časové pásmo</string>
+  <string name="subminute">"méně než 1 minuta"</string>
+  <string name="time">Čas:</string>
+</resources>
diff --git a/res/values-de-rDE/strings.xml b/res/values-de-rDE/strings.xml
new file mode 100644 (file)
index 0000000..223ba6e
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="add_alarm">Wecker hinzufügen</string>
+  <string name="alarmClock">Wecker</string>
+  <string name="alarmSet">Weckereinstellung</string>
+  <string name="alarm_alert_dismiss_text">Deaktivieren</string>
+  <string name="alarm_alert_dismissed">Wecker deaktiviert</string>
+  <string name="alarm_alert_snooze_not_set">Erinnerung nicht eingestellt -- nächster Weckton eingestellt für <xliff:g id="time">%s</xliff:g></string>
+  <string name="alarm_alert_snooze_set">Erinnerung für <xliff:g id="minutes">%d</xliff:g> Minuten.</string>
+  <string name="alarm_alert_snooze_text">Erinnern</string>
+  <string name="alarm_repeat">Wiederholen</string>
+  <string name="alarm_set">Dieser Wecker ist von jetzt ab für <xliff:g id="time_delta">%s</xliff:g> eingestellt.</string>
+  <string name="alarm_vibrate">Vibrieren</string>
+  <string name="alert">Klingelton</string>
+  <string name="am">AM</string>
+  <string name="and">" und "</string>
+  <string name="clock_instructions">Wählen Sie eine Uhr zur Anzeige aus.</string>
+  <string name="combiner"><xliff:g id="xxx">%1$s</xliff:g><xliff:g id="xxx">%2$s</xliff:g><xliff:g id="xxx">%3$s</xliff:g><xliff:g id="xxx">%4$s</xliff:g><xliff:g id="xxx">%5$s</xliff:g></string>
+  <string name="day">"1 Tag"</string>
+  <string name="day_concat">", "</string>
+  <string name="days">"<xliff:g id="days">%s</xliff:g> Tage"</string>
+  <string name="dberror">Der Wecker konnte leider nicht eingestellt werden.</string>
+  <string name="delete_alarm">Wecker löschen</string>
+  <string name="enable">Wecker</string>
+  <string name="error">Wecker nicht eingestellt</string>
+  <string name="every_day">Jeden Tag</string>
+  <string name="hide_clock">Uhr ausblenden</string>
+  <string name="hour">1 Stunde</string>
+  <string name="hours"><xliff:g id="hours">%s</xliff:g> Stunden</string>
+  <string name="minute">"1 Minute"</string>
+  <string name="minutes">"<xliff:g id="minutes">%s</xliff:g> Minuten"</string>
+  <string name="never">Niemals</string>
+  <string name="nextAlarm">Nächster Wecker: <xliff:g id="next_alarm_time">%s</xliff:g></string>
+  <string name="pm">PM</string>
+  <string name="set_alarm">Wecker einstellen</string>
+  <string name="settings">Datum- &amp; Uhrzeit-Einstellungen</string>
+  <string name="show_clock">Uhr anzeigen</string>
+  <string name="snoozeSet">Erinnerungseinstellung</string>
+  <string name="space">" "</string>
+  <string name="subminute">"weniger als 1 Minute"</string>
+  <string name="time">Zeit</string>
+</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
new file mode 100644 (file)
index 0000000..01dc7b6
--- /dev/null
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+  <string name="add_alarm">Add alarm</string>
+  <string name="alarmClock">Alarm Clock</string>
+  <string name="alarmSet">Alarm set</string>
+  <string name="alarm_alert_dismiss_text">Dismiss</string>
+  <string name="alarm_alert_dismissed">Alarm dismissed</string>
+  <string name="alarm_alert_snooze_not_set">Snooze not set -- next alarm set for <xliff:g id="time">%s</xliff:g></string>
+  <string name="alarm_alert_snooze_set">Snoozing for <xliff:g id="minutes">%d</xliff:g> minutes.</string>
+  <string name="alarm_alert_snooze_text">Snooze</string>
+  <string name="alarm_repeat">Repeat</string>
+  <string name="alarm_set">This alarm is set for <xliff:g id="time_delta">%s</xliff:g> from now.</string>
+  <string name="alarm_vibrate">Vibrate</string>
+  <string name="alert">Ringtone</string>
+  <string name="am">am</string>
+  <string name="and">" and "</string>
+  <string name="clock_instructions">Select a clock to display.</string>
+  <string name="combiner"><xliff:g id="xxx">%1$s</xliff:g><xliff:g id="xxx">%2$s</xliff:g><xliff:g id="xxx">%3$s</xliff:g><xliff:g id="xxx">%4$s</xliff:g><xliff:g id="xxx">%5$s</xliff:g></string>
+  <string name="day">"1 day"</string>
+  <string name="day_concat">", "</string>
+  <string name="days">"<xliff:g id="days">%s</xliff:g> days"</string>
+  <string name="dberror">Sorry, the alarm could not be set.</string>
+  <string name="delete_alarm">Delete alarm</string>
+  <string name="enable">Alarm</string>
+  <string name="error">Alarm not set</string>
+  <string name="every_day">every day</string>
+  <string name="hide_clock">Hide clock</string>
+  <string name="hour">1 hour</string>
+  <string name="hours"><xliff:g id="hours">%s</xliff:g> hours</string>
+  <string name="minute">"1 minute"</string>
+  <string name="minutes">"<xliff:g id="minutes">%s</xliff:g> minutes"</string>
+  <string name="never">Never</string>
+  <string name="nextAlarm">Next alarm: <xliff:g id="next_alarm_time">%s</xliff:g></string>
+  <string name="pm">pm</string>
+  <string name="set_alarm">Set alarm</string>
+  <string name="settings">Date &amp; time settings</string>
+  <string name="show_clock">Show clock</string>
+  <string name="snoozeSet">Snooze set</string>
+  <string name="space">" "</string>
+  <string name="subminute">"less than 1 minute"</string>
+  <string name="time">Time</string>
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
new file mode 100644 (file)
index 0000000..f23aeef
--- /dev/null
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<resources>
+    <color name="ampm_off">#ff404040</color>
+    <color name="ampm_on">#ffffffff</color>
+
+    <color name="white">#ffffffff</color>
+    <color name="ltgrey">#ffe0e0e0</color>
+    <color name="black">#ff000000</color>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644 (file)
index 0000000..76433b2
--- /dev/null
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="alarmClock">Alarm Clock</string>
+    <string name="app_label">Alarm Clock</string>
+    <!-- settings -->
+    <string name="settings">Date &amp; time settings</string>
+    <!-- Menu items-->
+    <string name="add_alarm">Add alarm</string>
+    <string name="delete_alarm">Delete alarm</string>
+    <string name="show_clock">Show clock</string>
+    <string name="hide_clock">Hide clock</string>
+    <string name="set_alarm">Set alarm</string>
+    <!-- Setting labels on Set alarm screen-->
+    <string name="enable">Alarm</string>
+    <string name="alarm_vibrate">Vibrate</string>
+    <string name="alarm_repeat">Repeat</string>
+    <string name="alert">Ringtone</string>
+    <string name="time">Time</string>
+
+    <!-- Button lables that appear on the alarm dialog -->
+    <string name="alarm_alert_dismiss_text">Dismiss</string>
+    <string name="alarm_alert_snooze_text">Snooze</string>
+    <!-- Toasts that appear after Alarm is snoozed from the Alarm dialog -->
+    <string name="alarm_alert_snooze_set">Snoozing for <xliff:g id="minutes">%d</xliff:g> minutes.</string>
+    <string name="alarm_alert_snooze_not_set">Snooze not set -- next alarm set for <xliff:g id="time">%s</xliff:g></string>
+    <string name="alarm_alert_dismissed">Alarm dismissed</string>
+
+    <!-- Text for alarm confirmation toasts -->
+    <string name="alarm_set">This alarm is set for <xliff:g id="time_delta">%s</xliff:g> from now.</string>
+    <string name="combiner"><xliff:g id="xxx">%1$s</xliff:g><xliff:g id="xxx">%2$s</xliff:g><xliff:g id="xxx">%3$s</xliff:g><xliff:g id="xxx">%4$s</xliff:g><xliff:g id="xxx">%5$s</xliff:g></string>
+    <string name="day">"1 day"</string>
+    <string name="days">"<xliff:g id="days">%s</xliff:g> days"</string>
+    <string name="hour">1 hour</string>
+    <string name="hours"><xliff:g id="hours">%s</xliff:g> hours</string>
+    <string name="and">" and "</string>
+    <string name="space">" "</string>
+    <string name="subminute">"less than 1 minute"</string>
+    <string name="minute">"1 minute"</string>
+    <string name="minutes">"<xliff:g id="minutes">%s</xliff:g> minutes"</string>
+
+    <string name="am">am</string>
+    <string name="pm">pm</string>
+
+    <!-- Status bar notifications-->
+    <string name="alarmSet">Alarm set</string>
+    <string name="snoozeSet">Snooze set</string>
+    <string name="nextAlarm">Next alarm: <xliff:g id="next_alarm_time">%s</xliff:g></string>
+
+    <!-- Repeat options that appear under an alarm on main Alarm Clock screen to identify repetition schedule -->
+    <string name="every_day">every day</string>
+    <string name="never">Never</string>
+    <string name="day_concat">", "</string>
+    <!-- Appears at the top of the screen after you select the clock on the main Alarm Clock screen -->
+    <string name="clock_instructions">Select a clock to display.</string>
+
+    <!-- error dialog title-->
+    <string name="error">Alarm not set</string>
+    <!-- error dialog message -->
+    <string name="dberror">Sorry, the alarm could not be set.</string>
+
+    <!-- Days of week -->
+    <string-array name="days_of_week">
+        <item>Monday</item>
+        <item>Tuesday</item>
+        <item>Wednesday</item>
+        <item>Thursday</item>
+        <item>Friday</item>
+        <item>Saturday</item>
+        <item>Sunday</item>
+    </string-array>
+    <string-array name="days_of_week_short">
+        <item>Mon</item>
+        <item>Tue</item>
+        <item>Wed</item>
+        <item>Thu</item>
+        <item>Fri</item>
+        <item>Sat</item>
+        <item>Sun</item>
+    </string-array>
+
+</resources>
+
+
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644 (file)
index 0000000..c00c6f5
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<resources>
+    <style name="analogClock">
+        <item name="android:layout_width">fill_parent</item>
+        <item name="android:layout_height">fill_parent</item>
+        <item name="android:layout_gravity">center_horizontal</item>
+    </style>
+</resources>
diff --git a/res/xml/alarm_prefs.xml b/res/xml/alarm_prefs.xml
new file mode 100644 (file)
index 0000000..d9e1243
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+    <PreferenceCategory android:title="@string/set_alarm"/>
+    <CheckBoxPreference android:key="on" 
+        android:title="@string/enable"/>
+    <Preference android:key="time" 
+        android:title="@string/time"/>
+    <com.android.alarmclock.AlarmPreference
+        android:key="alarm" 
+        android:title="@string/alert"
+        android:ringtoneType="alarm"
+        android:showDefault="false"
+        android:showSilent="false" />
+    <CheckBoxPreference android:key="vibrate" 
+        android:title="@string/alarm_vibrate"/>
+    <com.android.alarmclock.RepeatPreference
+        android:key="setRepeat" 
+        android:title="@string/alarm_repeat"
+        android:entries="@array/days_of_week"
+        android:entryValues="@array/days_of_week"/>
+</PreferenceScreen>
diff --git a/src/com/android/alarmclock/AlarmAlert.java b/src/com/android/alarmclock/AlarmAlert.java
new file mode 100644 (file)
index 0000000..31da676
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.app.Activity;
+import android.app.KeyguardManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.LayoutInflater;
+import android.widget.Button;
+import android.widget.Toast;
+
+import java.util.Calendar;
+
+/**
+ * Alarm Clock alarm alert: pops visible indicator and plays alarm
+ * tone
+ */
+public class AlarmAlert extends Activity implements Alarms.AlarmSettings {
+
+    private static long[] sVibratePattern = new long[] { 500, 500 };
+
+    private NotificationManager mNotificationManager;
+
+    private final static int AUDIO_ALERT_NOTIFICATION_ID = 0;
+    /** Play alarm up to 5 minutes before silencing */
+    private final static int ALARM_TIMEOUT_SECONDS = 300;
+    private final static int SNOOZE_MINUTES = 10;
+
+    private KeyguardManager mKeyguardManager;
+    private KeyguardManager.KeyguardLock mKeyguardLock = null;
+    private Handler mTimeout;
+    private Button mSnoozeButton;
+
+    private int mAlarmId;
+    private Alarms.DaysOfWeek mDaysOfWeek;
+    private String mAlert;
+    private boolean mVibrate;
+    private boolean mSnoozed;
+    private boolean mCleanupCalled = false;
+
+    public void reportAlarm(
+            int idx, boolean enabled, int hour, int minutes,
+            Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String message,
+            String alert) {
+        if (Log.LOGV) Log.v("AlarmAlert.reportAlarm: " + idx + " " + hour +
+                            " " + minutes + " dow " + daysOfWeek);
+        mAlert = alert;
+        mDaysOfWeek = daysOfWeek;
+        mVibrate = vibrate;
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.alarm_alert);
+
+        mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
+
+        /* set clock face */
+        LayoutInflater mFactory = LayoutInflater.from(this);
+        SharedPreferences settings = getSharedPreferences(AlarmClock.PREFERENCES, 0);
+        int face = settings.getInt(AlarmClock.PREF_CLOCK_FACE, 0);
+        if (face < 0 || face >= AlarmClock.CLOCKS.length) face = 0;
+        View clockLayout = (View)mFactory.inflate(AlarmClock.CLOCKS[face], null);
+        ViewGroup clockView = (ViewGroup)findViewById(R.id.clockView);
+        clockView.addView(clockLayout);
+        if (clockLayout instanceof DigitalClock) {
+            ((DigitalClock)clockLayout).setAnimate();
+        }
+
+        playAlert(getIntent());
+
+        /* allow next alarm to trigger while this activity is
+           active */
+        Alarms.disableSnoozeAlert(AlarmAlert.this);
+        Alarms.disableAlert(AlarmAlert.this, mAlarmId);
+        Alarms.setNextAlert(this);
+
+        /* snooze behavior: pop a snooze confirmation view, kick alarm
+           manager. */
+        mSnoozeButton = (Button) findViewById(R.id.snooze);
+        mSnoozeButton.requestFocus();
+        mSnoozeButton.setOnClickListener(new Button.OnClickListener() {
+            public void onClick(View v) {
+                /* If next alarm is set for sooner than the snooze interval,
+                   don't snooze: instead toast user that snooze will not be set */
+                final long snoozeTarget = System.currentTimeMillis() + 1000 * 60 * SNOOZE_MINUTES;
+                long nextAlarm = Alarms.calculateNextAlert(AlarmAlert.this).getAlert();
+                if (nextAlarm < snoozeTarget) {
+                    Calendar c = Calendar.getInstance();
+                    c.setTimeInMillis(nextAlarm);
+                    Toast.makeText(AlarmAlert.this,
+                                   getString(R.string.alarm_alert_snooze_not_set,
+                                             Alarms.formatTime(AlarmAlert.this, c)),
+                                   Toast.LENGTH_LONG).show();
+                } else {
+                    Toast.makeText(AlarmAlert.this,
+                                   getString(R.string.alarm_alert_snooze_set,
+                                             SNOOZE_MINUTES),
+                                   Toast.LENGTH_LONG).show();
+
+                    Alarms.saveSnoozeAlert(AlarmAlert.this, mAlarmId, snoozeTarget);
+                    Alarms.setNextAlert(AlarmAlert.this);
+                    mSnoozed = true;
+                }
+                disableKiller();
+                cleanupAlarm();
+                releaseLocks();
+                finish();
+            }
+        });
+
+        /* dismiss button: close notification */
+        findViewById(R.id.dismiss).setOnClickListener(new Button.OnClickListener() {
+                public void onClick(View v) {
+                    disableKiller();
+                    cleanupAlarm();
+                    releaseLocks();
+                    finish();
+                }
+            });
+    }
+
+    /**
+     * this is called when a second alarm is triggered while a
+     * previous alert window is still active.
+     */
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        if (Log.LOGV) Log.v("AlarmAlert.OnNewIntent()");
+        mSnoozeButton.setEnabled(true);
+        disableKeyguard();
+        cleanupAlarm();
+        mCleanupCalled = false;
+        disableKiller();
+        playAlert(intent);
+        Alarms.setNextAlert(this);
+        setIntent(intent);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (Log.LOGV) Log.v("AlarmAlert.onResume()");
+        disableKeyguard();
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        if (Log.LOGV) Log.v("AlarmAlert.onStop()");
+        disableKiller();
+        cleanupAlarm();
+        releaseLocks();
+    }
+
+    /**
+     * kicks off audio/vibe alert
+     */
+    private void playAlert(Intent intent) {
+        mAlarmId = intent.getIntExtra(Alarms.ID, 0);
+        if (Log.LOGV) Log.v("playAlert() " + mAlarmId);
+
+        /* load audio alert */
+        ContentResolver cr = getContentResolver();
+        /* this will call reportAlarm() callback */
+        Alarms.getAlarm(cr, this, mAlarmId);
+
+        /* play audio alert */
+        if (mAlert == null) {
+            Log.e("Unable to play alarm: no audio file available");
+        } else {
+            Notification audio = new Notification();
+            audio.sound = Uri.parse(mAlert);
+            audio.audioStreamType = AudioManager.STREAM_ALARM;
+            audio.flags |= Notification.FLAG_INSISTENT;
+            if (mVibrate) audio.vibrate = sVibratePattern;
+            mNotificationManager.notify(AUDIO_ALERT_NOTIFICATION_ID, audio);
+        }
+        enableKiller();
+    }
+
+    /**
+     * Kills alarm audio after ALARM_TIMEOUT_SECONDS, so the alarm
+     * won't run all day.
+     *
+     * This just cancels the audio, but leaves the notification
+     * popped, so the user will know that the alarm tripped.
+     */
+    private void enableKiller() {
+        mTimeout = new Handler();
+        mTimeout.postDelayed(new Runnable() {
+                public void run() {
+                    if (Log.LOGV) Log.v("*********** Alarm killer triggered *************");
+                    /* don't allow snooze */
+                    mSnoozeButton.setEnabled(false);
+                    cleanupAlarm();
+                    releaseLocks();
+                }}, 1000 * ALARM_TIMEOUT_SECONDS);
+    }
+
+    private void disableKiller() {
+        if (mTimeout != null) {
+            mTimeout.removeCallbacksAndMessages(null);
+            mTimeout = null;
+        }
+    }
+
+    private synchronized void enableKeyguard() {
+        if (mKeyguardLock != null) {
+            mKeyguardLock.reenableKeyguard();
+            mKeyguardLock = null;
+        }
+    }
+
+    private synchronized void disableKeyguard() {
+        if (mKeyguardLock == null) {
+            mKeyguardLock = mKeyguardManager.newKeyguardLock(Log.LOGTAG);
+            mKeyguardLock.disableKeyguard();
+        }
+    }
+
+    /**
+     * release wake and keyguard locks
+     */
+    private synchronized void releaseLocks() {
+        AlarmAlertWakeLock.release();
+        enableKeyguard();
+    }
+
+    /**
+     * Stops alarm audio and disables alarm if it not snoozed and not
+     * repeating
+     */
+    private synchronized void cleanupAlarm() {
+        if (Log.LOGV) Log.v("cleanupAlarm " + mAlarmId);
+        if (!mCleanupCalled) {
+            mCleanupCalled = true;
+
+            // Stop audio playing
+            mNotificationManager.cancel(AUDIO_ALERT_NOTIFICATION_ID);
+
+            /* disable alarm only if it is not set to repeat */
+            if (!mSnoozed && ((mDaysOfWeek == null || !mDaysOfWeek.isRepeatSet()))) {
+                Alarms.enableAlarm(AlarmAlert.this, mAlarmId, false);
+            }
+        }
+    }
+}
diff --git a/src/com/android/alarmclock/AlarmAlertWakeLock.java b/src/com/android/alarmclock/AlarmAlertWakeLock.java
new file mode 100644 (file)
index 0000000..bafc708
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * Hold a wakelock that can be acquired in the AlarmReceiver and
+ * released in the AlarmAlert activity
+ */
+class AlarmAlertWakeLock {
+
+    private static PowerManager.WakeLock sWakeLock;
+
+    static void acquire(Context context) {
+        if (sWakeLock != null) {
+            sWakeLock.release();
+        }
+
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+
+        sWakeLock = pm.newWakeLock(
+                PowerManager.FULL_WAKE_LOCK |
+                PowerManager.ACQUIRE_CAUSES_WAKEUP |
+                PowerManager.ON_AFTER_RELEASE, Log.LOGTAG);
+        sWakeLock.acquire();
+    }
+
+    static void release() {
+        if (Log.LOGV) Log.v("AlarmAlertWakeLock release");
+        if (sWakeLock != null) {
+            sWakeLock.release();
+            sWakeLock = null;
+        }
+    }
+}
diff --git a/src/com/android/alarmclock/AlarmClock.java b/src/com/android/alarmclock/AlarmClock.java
new file mode 100644 (file)
index 0000000..8db3660
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.CheckBox;
+
+import java.util.Calendar;
+
+/**
+ * AlarmClock application.
+ */
+public class AlarmClock extends Activity {
+
+    final static String PREFERENCES = "AlarmClock";
+    final static int SET_ALARM = 1;
+    final static String PREF_CLOCK_FACE = "face";
+    final static String PREF_SHOW_CLOCK = "show_clock";
+
+    /** Cap alarm count at this number */
+    final static int MAX_ALARM_COUNT = 12;
+
+    private SharedPreferences mPrefs;
+    private LayoutInflater mFactory;
+    private ViewGroup mClockLayout;
+    private View mClock = null;
+    private MenuItem mAddAlarmItem;
+    private MenuItem mToggleClockItem;
+    private ListView mAlarmsList;
+    private Cursor mCursor;
+
+    /**
+     * Which clock face to show
+     */
+    private int mFace = -1;
+
+    /*
+     * FIXME: it would be nice for this to live in an xml config file.
+     */
+    final static int[] CLOCKS = {
+        R.layout.clock_basic_bw,
+        R.layout.clock_googly,
+        R.layout.clock_droid2,
+        R.layout.clock_droids,
+        R.layout.digital_clock
+    };
+
+    private class AlarmTimeAdapter extends CursorAdapter {
+        public AlarmTimeAdapter(Context context, Cursor cursor) {
+            super(context, cursor);
+        }
+
+        public View newView(Context context, Cursor cursor, ViewGroup parent) {
+            /* FIXME: this is called 3+x times too many by the ListView */
+            View ret = mFactory.inflate(R.layout.alarm_time, parent, false);
+            DigitalClock digitalClock = (DigitalClock)ret.findViewById(R.id.digitalClock);
+            digitalClock.setLive(false);
+            if (Log.LOGV) Log.v("newView " + cursor.getPosition());
+            return ret;
+        }
+
+        public void bindView(View view, Context context, Cursor cursor) {
+            final int id = cursor.getInt(Alarms.AlarmColumns.ALARM_ID_INDEX);
+            final int hour = cursor.getInt(Alarms.AlarmColumns.ALARM_HOUR_INDEX);
+            final int minutes = cursor.getInt(Alarms.AlarmColumns.ALARM_MINUTES_INDEX);
+            final Alarms.DaysOfWeek daysOfWeek = new Alarms.DaysOfWeek(
+                    cursor.getInt(Alarms.AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX));
+            final boolean enabled = cursor.getInt(Alarms.AlarmColumns.ALARM_ENABLED_INDEX) == 1;
+
+            CheckBox onButton = (CheckBox)view.findViewById(R.id.alarmButton);
+            onButton.setChecked(enabled);
+            onButton.setOnClickListener(new OnClickListener() {
+                    public void onClick(View v) {
+                        boolean isChecked = ((CheckBox) v).isChecked();
+                        Alarms.enableAlarm(AlarmClock.this, id, isChecked);
+                        if (isChecked) {
+                            SetAlarm.popAlarmSetToast(
+                                    AlarmClock.this, hour, minutes, daysOfWeek);
+                        }
+                    }
+            });
+
+            DigitalClock digitalClock = (DigitalClock)view.findViewById(R.id.digitalClock);
+            if (Log.LOGV) Log.v("bindView " + cursor.getPosition() + " " + id + " " + hour +
+                                ":" + minutes + " " + daysOfWeek.toString(context, true) + " dc " + digitalClock);
+
+            digitalClock.setOnClickListener(new OnClickListener() {
+                    public void onClick(View v) {
+                        if (true) {
+                            Intent intent = new Intent(AlarmClock.this, SetAlarm.class);
+                            intent.putExtra(Alarms.ID, id);
+                            startActivityForResult(intent, SET_ALARM);
+                        } else {
+                            // TESTING: immediately pop alarm
+                            Intent fireAlarm = new Intent(AlarmClock.this, AlarmAlert.class);
+                            fireAlarm.putExtra(Alarms.ID, id);
+                            fireAlarm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                            startActivity(fireAlarm);
+                        }
+                    }
+                });
+
+            // set the alarm text
+            Calendar c = Calendar.getInstance();
+            c.set(Calendar.HOUR_OF_DAY, hour);
+            c.set(Calendar.MINUTE, minutes);
+            digitalClock.updateTime(c);
+            TextView daysOfWeekView = (TextView) digitalClock.findViewById(R.id.daysOfWeek);
+            daysOfWeekView.setText(daysOfWeek.toString(AlarmClock.this, false));
+        }
+    };
+
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        // sanity check -- no database, no clock
+        if (getContentResolver() == null) {
+            new AlertDialog.Builder(this)
+                    .setTitle(getString(R.string.error))
+                    .setMessage(getString(R.string.dberror))
+                    .setPositiveButton(
+                            android.R.string.ok,
+                            new DialogInterface.OnClickListener() {
+                                public void onClick(DialogInterface dialog, int which) {
+                                    finish();
+                                }
+                            })
+                    .setOnCancelListener(
+                            new DialogInterface.OnCancelListener() {
+                                public void onCancel(DialogInterface dialog) {
+                                    finish();
+                                }})
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .create().show();
+            return;
+        }
+
+        setContentView(R.layout.alarm_clock);
+        mFactory = LayoutInflater.from(this);
+        mPrefs = getSharedPreferences(PREFERENCES, 0);
+
+        mCursor = Alarms.getAlarmsCursor(getContentResolver());
+        mAlarmsList = (ListView) findViewById(R.id.alarms_list);
+        mAlarmsList.setAdapter(new AlarmTimeAdapter(this, mCursor));
+        mAlarmsList.setVerticalScrollBarEnabled(true);
+        mAlarmsList.setItemsCanFocus(true);
+
+        mClockLayout = (ViewGroup) findViewById(R.id.clock_layout);
+        mClockLayout.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    final Intent intent = new Intent(AlarmClock.this, ClockPicker.class);
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                    startActivity(intent);
+                }
+            });
+
+        setClockVisibility(mPrefs.getBoolean(PREF_SHOW_CLOCK, true));
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        int face = mPrefs.getInt(PREF_CLOCK_FACE, 0);
+        if (mFace != face) {
+            if (face < 0 || face >= AlarmClock.CLOCKS.length)
+                mFace = 0;
+            else
+                mFace = face;
+            inflateClock();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        ToastMaster.cancelToast();
+        mCursor.deactivate();
+    }
+
+    protected void inflateClock() {
+        if (mClock != null) {
+            mClockLayout.removeView(mClock);
+        }
+        mClock = mFactory.inflate(CLOCKS[mFace], null);
+        mClockLayout.addView(mClock, 0);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+
+        mAddAlarmItem = menu.add(0, 0, 0, R.string.add_alarm);
+        mAddAlarmItem.setIcon(android.R.drawable.ic_menu_add);
+
+        mToggleClockItem = menu.add(0, 0, 0, R.string.hide_clock);
+        mToggleClockItem.setIcon(R.drawable.ic_menu_clock_face);
+
+        if (false) {
+            Intent intent = new Intent();
+            intent.setClassName(
+                    "com.android.settings",
+                    "com.android.settings.DateTimeSettings");
+            MenuItem settingsItem = menu.add(0, 0, 0, R.string.settings).setIntent(intent);
+            settingsItem.setIcon(android.R.drawable.ic_menu_preferences);
+        }
+
+        return true;
+    }
+
+    /**
+     * Only allow user to add a new alarm if there are fewer than
+     * MAX_ALARM_COUNT
+     */
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        mAddAlarmItem.setVisible(mAlarmsList.getChildCount() < MAX_ALARM_COUNT);
+        mToggleClockItem.setTitle(getClockVisibility() ? R.string.hide_clock :
+                                  R.string.show_clock);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item == mAddAlarmItem) {
+            Uri uri = Alarms.addAlarm(getContentResolver());
+            // FIXME: scroll to new item.  mAlarmsList.requestChildRectangleOnScreen() ?
+            String segment = uri.getPathSegments().get(1);
+            int newId = Integer.parseInt(segment);
+            if (Log.LOGV) Log.v("In AlarmClock, new alarm id = " + newId);
+            Intent intent = new Intent(AlarmClock.this, SetAlarm.class);
+            intent.putExtra(Alarms.ID, newId);
+            startActivityForResult(intent, SET_ALARM);
+            return true;
+        } else if (item == mToggleClockItem) {
+            setClockVisibility(!getClockVisibility());
+            saveClockVisibility();
+            return true;
+        }
+
+        return false;
+    }
+
+
+    private boolean getClockVisibility() {
+        return mClockLayout.getVisibility() == View.VISIBLE;
+    }
+
+    private void setClockVisibility(boolean visible) {
+        mClockLayout.setVisibility(visible ? View.VISIBLE : View.GONE);
+    }
+
+    private void saveClockVisibility() {
+        mPrefs.edit().putBoolean(PREF_SHOW_CLOCK, getClockVisibility()).commit();
+    }
+}
diff --git a/src/com/android/alarmclock/AlarmInitReceiver.java b/src/com/android/alarmclock/AlarmInitReceiver.java
new file mode 100644 (file)
index 0000000..77549b0
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+
+public class AlarmInitReceiver extends BroadcastReceiver {
+
+    /**
+     * Sets alarm on ACTION_BOOT_COMPLETED.  Resets alarm on
+     * TIME_SET, TIMEZONE_CHANGED
+     */
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (Log.LOGV) Log.v("AlarmInitReceiver" + action);
+
+        if (context.getContentResolver() == null) {
+            Log.e("AlarmInitReceiver: FAILURE unable to get content resolver.  Alarms inactive.");
+            return;
+        }
+        if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
+            Alarms.disableSnoozeAlert(context);
+            Alarms.disableExpiredAlarms(context);
+        }
+        Alarms.setNextAlert(context);
+    }
+}
diff --git a/src/com/android/alarmclock/AlarmPreference.java b/src/com/android/alarmclock/AlarmPreference.java
new file mode 100644 (file)
index 0000000..0fd4f89
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.content.Context;
+import android.net.Uri;
+import android.preference.RingtonePreference;
+import android.util.AttributeSet;
+
+public class AlarmPreference extends RingtonePreference {
+    public Uri mAlert;
+    private IRingtoneChangedListener mRingtoneChangedListener;
+
+    public interface IRingtoneChangedListener {
+        public void onRingtoneChanged(Uri ringtoneUri);
+    };
+
+    public AlarmPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setRingtoneChangedListener(IRingtoneChangedListener listener) {
+        mRingtoneChangedListener = listener;
+    }
+
+    @Override
+    protected void onSaveRingtone(Uri ringtoneUri) {
+        if (ringtoneUri != null) {
+            mAlert = ringtoneUri;
+            if (mRingtoneChangedListener != null) {
+                mRingtoneChangedListener.onRingtoneChanged(ringtoneUri);
+            }
+        }
+    }
+
+    @Override
+    protected Uri onRestoreRingtone() {
+        return mAlert;
+    }
+}
diff --git a/src/com/android/alarmclock/AlarmProvider.java b/src/com/android/alarmclock/AlarmProvider.java
new file mode 100644 (file)
index 0000000..407f577
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.text.TextUtils;
+
+public class AlarmProvider extends ContentProvider {
+    private SQLiteOpenHelper mOpenHelper;
+
+    private static final int ALARMS = 1;
+    private static final int ALARMS_ID = 2;
+    private static final UriMatcher sURLMatcher = new UriMatcher(
+            UriMatcher.NO_MATCH);
+
+    static {
+        sURLMatcher.addURI("com.android.alarmclock", "alarm", ALARMS);
+        sURLMatcher.addURI("com.android.alarmclock", "alarm/#", ALARMS_ID);
+    }
+
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+        private static final String DATABASE_NAME = "alarms.db";
+        private static final int DATABASE_VERSION = 5;
+
+        public DatabaseHelper(Context context) {
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE alarms (" +
+                       "_id INTEGER PRIMARY KEY," +
+                       "hour INTEGER, " +
+                       "minutes INTEGER, " +
+                       "daysofweek INTEGER, " +
+                       "alarmtime INTEGER, " +
+                       "enabled INTEGER, " +
+                       "vibrate INTEGER, " +
+                       "message TEXT, " +
+                       "alert TEXT);");
+
+            // insert default alarms
+            String insertMe = "INSERT INTO alarms " +
+                    "(hour, minutes, daysofweek, alarmtime, enabled, vibrate, message, alert) " +
+                    "VALUES ";
+            db.execSQL(insertMe + "(7, 0, 0, 0, 0, 1, '', '');");
+            db.execSQL(insertMe + "(8, 30, 2, 0, 0, 1, '', '');");
+            db.execSQL(insertMe + "(9, 00, 18, 0, 0, 1, '', '');");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int currentVersion) {
+            if (Log.LOGV) Log.v(
+                    "Upgrading alarms database from version " +
+                    oldVersion + " to " + currentVersion +
+                    ", which will destroy all old data");
+            db.execSQL("DROP TABLE IF EXISTS alarms");
+            onCreate(db);
+        }
+    }
+
+    public AlarmProvider() {
+    }
+
+    @Override
+    public boolean onCreate() {
+        mOpenHelper = new DatabaseHelper(getContext());
+        return true;
+    }
+
+    @Override
+    public Cursor query(Uri url, String[] projectionIn, String selection,
+            String[] selectionArgs, String sort) {
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+
+        // Generate the body of the query
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case ALARMS:
+                qb.setTables("alarms");
+                break;
+            case ALARMS_ID:
+                qb.setTables("alarms");
+                qb.appendWhere("_id=");
+                qb.appendWhere(url.getPathSegments().get(1));
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown URL " + url);
+        }
+
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor ret = qb.query(db, projectionIn, selection, selectionArgs,
+                              null, null, sort);
+
+        if (ret == null) {
+            if (Log.LOGV) Log.v("Alarms.query: failed");
+        } else {
+            ret.setNotificationUri(getContext().getContentResolver(), url);
+        }
+
+        return ret;
+    }
+
+    @Override
+    public String getType(Uri url) {
+        int match = sURLMatcher.match(url);
+        switch (match) {
+            case ALARMS:
+                return "vnd.android.cursor.dir/alarms";
+            case ALARMS_ID:
+                return "vnd.android.cursor.item/alarms";
+            default:
+                throw new IllegalArgumentException("Unknown URL");
+        }
+    }
+
+    @Override
+    public int update(Uri url, ContentValues values, String where, String[] whereArgs) {
+        int count;
+        long rowId = 0;
+        int match = sURLMatcher.match(url);
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        switch (match) {
+            case ALARMS_ID: {
+                String segment = url.getPathSegments().get(1);
+                rowId = Long.parseLong(segment);
+                count = db.update("alarms", values, "_id=" + rowId, null);
+                break;
+            }
+            default: {
+                throw new UnsupportedOperationException(
+                        "Cannot update URL: " + url);
+            }
+        }
+        if (Log.LOGV) Log.v("*** notifyChange() rowId: " + rowId + " url " + url);
+        getContext().getContentResolver().notifyChange(url, null);
+        return count;
+    }
+
+    @Override
+    public Uri insert(Uri url, ContentValues initialValues) {
+        if (sURLMatcher.match(url) != ALARMS) {
+            throw new IllegalArgumentException("Cannot insert into URL: " + url);
+        }
+
+        ContentValues values;
+        if (initialValues != null)
+            values = new ContentValues(initialValues);
+        else
+            values = new ContentValues();
+
+        if (!values.containsKey(Alarms.AlarmColumns.HOUR))
+            values.put(Alarms.AlarmColumns.HOUR, 0);
+
+        if (!values.containsKey(Alarms.AlarmColumns.MINUTES))
+            values.put(Alarms.AlarmColumns.MINUTES, 0);
+
+        if (!values.containsKey(Alarms.AlarmColumns.DAYS_OF_WEEK))
+            values.put(Alarms.AlarmColumns.DAYS_OF_WEEK, 0);
+
+        if (!values.containsKey(Alarms.AlarmColumns.ALARM_TIME))
+            values.put(Alarms.AlarmColumns.ALARM_TIME, 0);
+
+        if (!values.containsKey(Alarms.AlarmColumns.ENABLED))
+            values.put(Alarms.AlarmColumns.ENABLED, 0);
+
+        if (!values.containsKey(Alarms.AlarmColumns.VIBRATE))
+            values.put(Alarms.AlarmColumns.VIBRATE, 1);
+
+        if (!values.containsKey(Alarms.AlarmColumns.MESSAGE))
+            values.put(Alarms.AlarmColumns.MESSAGE, "");
+
+        if (!values.containsKey(Alarms.AlarmColumns.ALERT))
+            values.put(Alarms.AlarmColumns.ALERT, "");
+
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        long rowId = db.insert("alarms", Alarms.AlarmColumns.MESSAGE, values);
+        if (rowId < 0) {
+            throw new SQLException("Failed to insert row into " + url);
+        }
+        if (Log.LOGV) Log.v("Added alarm rowId = " + rowId);
+
+        Uri newUrl = ContentUris.withAppendedId(Alarms.AlarmColumns.CONTENT_URI, rowId);
+        getContext().getContentResolver().notifyChange(newUrl, null);
+        return newUrl;
+    }
+
+    public int delete(Uri url, String where, String[] whereArgs) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        int count;
+        long rowId = 0;
+        switch (sURLMatcher.match(url)) {
+            case ALARMS:
+                count = db.delete("alarms", where, whereArgs);
+                break;
+            case ALARMS_ID:
+                String segment = url.getPathSegments().get(1);
+                rowId = Long.parseLong(segment);
+                if (TextUtils.isEmpty(where)) {
+                    where = "_id=" + segment;
+                } else {
+                    where = "_id=" + segment + " AND (" + where + ")";
+                }
+                count = db.delete("alarms", where, whereArgs);
+                break;
+            default:
+                throw new IllegalArgumentException("Cannot delete from URL: " + url);
+        }
+
+        getContext().getContentResolver().notifyChange(url, null);
+        return count;
+    }
+}
diff --git a/src/com/android/alarmclock/AlarmReceiver.java b/src/com/android/alarmclock/AlarmReceiver.java
new file mode 100644 (file)
index 0000000..434e823
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.BroadcastReceiver;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.SystemClock;
+
+/**
+ * Glue class: connects AlarmAlert IntentReceiver to AlarmAlert
+ * activity.  Passes through Alarm ID.
+ */
+public class AlarmReceiver extends BroadcastReceiver {
+
+    /** If the alarm is older than STALE_WINDOW seconds, ignore.  It
+        is probably the result of a time or timezone change */
+    private final static int STALE_WINDOW = 60 * 30;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        long now = System.currentTimeMillis();
+        int id = intent.getIntExtra(Alarms.ID, 0);
+        long setFor = intent.getLongExtra(Alarms.TIME, 0);
+
+        if (now > setFor + STALE_WINDOW * 1000) {
+            if (Log.LOGV) Log.v("AlarmReceiver ignoring stale alarm intent id"
+                                + id + " setFor " + setFor + " now " + now);
+            return;
+        }
+
+        if (Log.LOGV) Log.v("*********** Keep UI Alive *************");
+        AlarmAlertWakeLock.acquire(context);
+
+        Intent fireAlarm = new Intent(context, AlarmAlert.class);
+
+        fireAlarm.putExtra(Alarms.ID, id);
+        fireAlarm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        context.startActivity(fireAlarm);
+   }
+}
diff --git a/src/com/android/alarmclock/Alarms.java b/src/com/android/alarmclock/Alarms.java
new file mode 100644 (file)
index 0000000..849f6b9
--- /dev/null
@@ -0,0 +1,753 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.app.AlarmManager;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.net.Uri;
+import android.pim.DateFormat;
+import android.provider.BaseColumns;
+import android.provider.Settings;
+
+import java.util.Calendar;
+
+/**
+ * The Alarms provider supplies info about Alarm Clock settings
+ */
+public class Alarms {
+
+    public final static String ALARM_ALERT_ACTION = "com.android.alarmclock.ALARM_ALERT";
+    public final static String ID = "alarm_id";
+    public final static String TIME = "alarm_time";
+    private final static int STATUSBAR_NOTIFICATION_ID = 22;
+
+    final static String PREF_SNOOZE_ID = "snooze_id";
+    final static String PREF_SNOOZE_TIME = "snooze_time";
+
+    private final static String DM12 = "E h:mm aa";
+    private final static String DM24 = "E k:mm";
+
+    private final static String M12 = "h:mm aa";
+    private final static String M24 = "k:mm";
+
+    static class DaysOfWeek {
+
+        int mDays;
+
+        /**
+         * Days of week coded as single int, convenient for DB
+         * storage:
+         *
+         * 0x00:  no day
+         * 0x01:  Monday
+         * 0x02:  Tuesday
+         * 0x04:  Wednesday
+         * 0x08:  Thursday
+         * 0x10:  Friday
+         * 0x20:  Saturday
+         * 0x40:  Sunday
+         */
+        DaysOfWeek() {
+            this(0);
+        }
+
+        DaysOfWeek(int days) {
+            mDays = days;
+        }
+
+        public String toString(Context context, boolean showNever) {
+            StringBuilder ret = new StringBuilder();
+
+            /* no days */
+            if (mDays == 0) return showNever ? context.getText(
+                    R.string.never).toString() : "";
+
+            /* every day */
+            if (mDays == 0x7f) {
+                return context.getText(R.string.every_day).toString();
+            }
+
+            /* count selected days */
+            int dayCount = 0, days = mDays;
+            while (days > 0) {
+                if ((days & 1) == 1) dayCount++;
+                days >>= 1;
+            }
+
+            /* short or long form? */
+            CharSequence[] strings =
+                    context.getResources().getTextArray(
+                            (dayCount > 1) ? R.array.days_of_week_short :
+                            R.array.days_of_week);
+
+            /* selected days */
+            for (int i = 0; i < 7; i++) {
+                if ((mDays & (1 << i)) != 0) {
+                    ret.append(strings[i]);
+                    dayCount -= 1;
+                    if (dayCount > 0) ret.append(
+                            context.getText(R.string.day_concat));
+                }
+            }
+            return ret.toString();
+        }
+
+        /**
+         * @param day Mon=0 ... Sun=6
+         * @return true if given day is set
+         */
+        public boolean isSet(int day) {
+            return ((mDays & (1 << day)) > 0);
+        }
+
+        public void set(int day, boolean set) {
+            if (set) {
+                mDays |= (1 << day);
+            } else {
+                mDays &= ~(1 << day);
+            }
+        }
+
+        public void set(DaysOfWeek dow) {
+            mDays = dow.mDays;
+        }
+
+        public int getCoded() {
+            return mDays;
+        }
+
+        public boolean equals(DaysOfWeek dow) {
+            return mDays == dow.mDays;
+        }
+
+        // Returns days of week encoded in an array of booleans.
+        public boolean[] getBooleanArray() {
+            boolean[] ret = new boolean[7];
+            for (int i = 0; i < 7; i++) {
+                ret[i] = isSet(i);
+            }
+            return ret;
+        }
+
+        public void setCoded(int days) {
+            mDays = days;
+        }
+
+        /**
+         * @return true if alarm is set to repeat
+         */
+        public boolean isRepeatSet() {
+            return mDays != 0;
+        }
+
+        /**
+         * @return true if alarm is set to repeat every day
+         */
+        public boolean isEveryDaySet() {
+            return mDays == 0x7f;
+        }
+
+
+        /**
+         * returns number of days from today until next alarm
+         * @param c must be set to today
+         */
+        public int getNextAlarm(Calendar c) {
+            if (mDays == 0) return -1;
+            int today = (c.get(Calendar.DAY_OF_WEEK) + 5) % 7;
+
+            int day, dayCount;
+            for (dayCount = 0; dayCount < 7; dayCount++) {
+                day = (today + dayCount) % 7;
+                if ((mDays & (1 << day)) > 0) {
+                    break;
+                }
+            }
+            return dayCount;
+        }
+    }
+
+    public static class AlarmColumns implements BaseColumns {
+
+        /**
+         * The content:// style URL for this table
+         */
+        public static final Uri CONTENT_URI =
+            Uri.parse("content://com.android.alarmclock/alarm");
+
+        public static final String _ID = "_id";
+
+        /**
+         * The default sort order for this table
+         */
+        public static final String DEFAULT_SORT_ORDER = "_id ASC";
+
+        /**
+         * 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 week coded as integer
+         * <P>Type: INTEGER</P>
+         */
+        public static final String DAYS_OF_WEEK = "daysofweek";
+
+        /**
+         * Alarm time in UTC milliseconds from the epoch.
+         * <P>Type: INTEGER</P>
+         */
+        public static final String ALARM_TIME = "alarmtime";
+
+        /**
+         * True if alarm is active
+         * <P>Type: BOOLEAN</P>
+         */
+        public static final String ENABLED = "enabled";
+
+        /**
+         * True if alarm should vibrate
+         * <P>Type: BOOLEAN</P>
+         */
+        public static final String VIBRATE = "vibrate";
+
+        /**
+         * Message to show when alarm triggers
+         * Note: not currently used
+         * <P>Type: STRING</P>
+         */
+        public static final String MESSAGE = "message";
+
+        /**
+         * Audio alert to play when alarm triggers
+         * <P>Type: STRING</P>
+         */
+        public static final String ALERT = "alert";
+
+        static final String[] ALARM_QUERY_COLUMNS = {
+            _ID, HOUR, MINUTES, DAYS_OF_WEEK, ALARM_TIME,
+            ENABLED, VIBRATE, MESSAGE, ALERT};
+
+        /**
+         * These save calls to cursor.getColumnIndexOrThrow()
+         * THEY MUST BE KEPT IN SYNC WITH ABOVE QUERY COLUMNS
+         */
+        public static final int ALARM_ID_INDEX = 0;
+        public static final int ALARM_HOUR_INDEX = 1;
+        public static final int ALARM_MINUTES_INDEX = 2;
+        public static final int ALARM_DAYS_OF_WEEK_INDEX = 3;
+        public static final int ALARM_TIME_INDEX = 4;
+        public static final int ALARM_ENABLED_INDEX = 5;
+        public static final int ALARM_VIBRATE_INDEX = 6;
+        public static final int ALARM_MESSAGE_INDEX = 7;
+        public static final int ALARM_ALERT_INDEX = 8;
+    }
+
+    /**
+     * getAlarm and getAlarms call this interface to report alarms in
+     * the database
+     */
+    static interface AlarmSettings {
+        void reportAlarm(
+                int idx, boolean enabled, int hour, int minutes,
+                DaysOfWeek daysOfWeek, boolean vibrate, String message,
+                String alert);
+    }
+
+    /**
+     * Creates a new Alarm.
+     */
+    public synchronized static Uri addAlarm(ContentResolver contentResolver) {
+        ContentValues values = new ContentValues();
+        values.put(Alarms.AlarmColumns.HOUR, 8);
+        return contentResolver.insert(AlarmColumns.CONTENT_URI, values);
+    }
+
+    /**
+     * Removes an existing Alarm.
+     */
+    public synchronized static void deleteAlarm(
+            ContentResolver contentResolver, int alarmId) {
+        Uri uri = ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, alarmId);
+        deleteAlarm(contentResolver, uri);
+    }
+
+    public synchronized static void deleteAlarm(
+            ContentResolver contentResolver, Uri uri) {
+        contentResolver.delete(uri, "", null);
+    }
+
+    /**
+     * Queries all alarms
+     * @return cursor over all alarms
+     */
+    public synchronized static Cursor getAlarmsCursor(
+            ContentResolver contentResolver) {
+        return contentResolver.query(
+                AlarmColumns.CONTENT_URI, AlarmColumns.ALARM_QUERY_COLUMNS,
+                null, null, AlarmColumns.DEFAULT_SORT_ORDER);
+    }
+
+    /**
+     * Calls the AlarmSettings.reportAlarm interface on all alarms found in db.
+     */
+    public synchronized static void getAlarms(
+            ContentResolver contentResolver, AlarmSettings alarmSettings) {
+        Cursor cursor = getAlarmsCursor(contentResolver);
+        getAlarms(alarmSettings, cursor);
+        cursor.close();
+    }
+
+    private synchronized static void getAlarms(
+            AlarmSettings alarmSettings, Cursor cur) {
+        if (cur.moveToFirst()) {
+            do {
+                // Get the field values
+                int id = cur.getInt(AlarmColumns.ALARM_ID_INDEX);
+                int hour = cur.getInt(AlarmColumns.ALARM_HOUR_INDEX);
+                int minutes = cur.getInt(AlarmColumns.ALARM_MINUTES_INDEX);
+                int daysOfWeek = cur.getInt(AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX);
+                boolean enabled = cur.getInt(AlarmColumns.ALARM_ENABLED_INDEX) == 1 ? true : false;
+                boolean vibrate = cur.getInt(AlarmColumns.ALARM_VIBRATE_INDEX) == 1 ? true : false;
+                String message = cur.getString(AlarmColumns.ALARM_MESSAGE_INDEX);
+                String alert = cur.getString(AlarmColumns.ALARM_ALERT_INDEX);
+                alarmSettings.reportAlarm(
+                        id, enabled, hour, minutes, new DaysOfWeek(daysOfWeek),
+                        vibrate, message, alert);
+            } while (cur.moveToNext());
+        }
+    }
+
+    /**
+     * Calls the AlarmSettings.reportAlarm interface on alarm with given
+     * alarmId
+     */
+    public synchronized static void getAlarm(
+            ContentResolver contentResolver, AlarmSettings alarmSetting,
+            int alarmId) {
+        Cursor cursor = contentResolver.query(
+                ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, alarmId),
+                AlarmColumns.ALARM_QUERY_COLUMNS,
+                null, null, AlarmColumns.DEFAULT_SORT_ORDER);
+
+        getAlarms(alarmSetting, cursor);
+        cursor.close();
+    }
+
+
+    /**
+     * A convenience method to set an alarm in the Alarms
+     * content provider.
+     *
+     * @param id             corresponds to the _id column
+     * @param enabled        corresponds to the ENABLED column
+     * @param hour           corresponds to the HOUR column
+     * @param minutes        corresponds to the MINUTES column
+     * @param daysOfWeek     corresponds to the DAYS_OF_WEEK column
+     * @param time           corresponds to the ALARM_TIME column
+     * @param vibrate        corresponds to the VIBRATE column
+     * @param message        corresponds to the MESSAGE column
+     * @param alert          corresponds to the ALERT column
+     */
+    public synchronized static void setAlarm(
+            Context context, int id, boolean enabled, int hour, int minutes,
+            DaysOfWeek daysOfWeek, boolean vibrate, String message,
+            String alert) {
+
+        ContentValues values = new ContentValues(8);
+        ContentResolver resolver = context.getContentResolver();
+        long time = calculateAlarm(hour, minutes, daysOfWeek).getTimeInMillis();
+
+        if (Log.LOGV) Log.v(
+                "**  setAlarm * idx " + id + " hour " + hour + " minutes " +
+                minutes + " enabled " + enabled + " time " + time);
+
+        values.put(AlarmColumns.ENABLED, enabled ? 1 : 0);
+        values.put(AlarmColumns.HOUR, hour);
+        values.put(AlarmColumns.MINUTES, minutes);
+        values.put(AlarmColumns.ALARM_TIME, time);
+        values.put(AlarmColumns.DAYS_OF_WEEK, daysOfWeek.getCoded());
+        values.put(AlarmColumns.VIBRATE, vibrate);
+        values.put(AlarmColumns.MESSAGE, message);
+        values.put(AlarmColumns.ALERT, alert);
+        resolver.update(ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, id),
+                        values, null, null);
+
+        int aid = disableSnoozeAlert(context);
+        if (aid != -1 && aid != id) enableAlarmInternal(context, aid, false);
+        setNextAlert(context);
+    }
+
+    /**
+     * A convenience method to enable or disable an alarm.
+     *
+     * @param id             corresponds to the _id column
+     * @param enabled        corresponds to the ENABLED column
+     */
+
+    public synchronized static void enableAlarm(
+            final Context context, final int id, boolean enabled) {
+        int aid = disableSnoozeAlert(context);
+        if (aid != -1 && aid != id) enableAlarmInternal(context, aid, false);
+        enableAlarmInternal(context, id, enabled);
+        setNextAlert(context);
+    }
+
+    private synchronized static void enableAlarmInternal(
+            final Context context, final int id, boolean enabled) {
+        ContentResolver resolver = context.getContentResolver();
+
+        class EnableAlarm implements AlarmSettings {
+            public int mHour;
+            public int mMinutes;
+            public DaysOfWeek mDaysOfWeek;
+
+            public void reportAlarm(
+                    int idx, boolean enabled, int hour, int minutes,
+                    DaysOfWeek daysOfWeek, boolean vibrate, String message,
+                    String alert) {
+                mHour = hour;
+                mMinutes = minutes;
+                mDaysOfWeek = daysOfWeek;
+            }
+        }
+
+        ContentValues values = new ContentValues(2);
+        values.put(AlarmColumns.ENABLED, enabled ? 1 : 0);
+
+        /* If we are enabling the alarm, load hour/minutes/daysOfWeek
+           from db, so we can calculate alarm time */
+        if (enabled) {
+            EnableAlarm enableAlarm = new EnableAlarm();
+            getAlarm(resolver, enableAlarm, id);
+            if (enableAlarm.mDaysOfWeek == null) {
+                /* Under monkey, sometimes reportAlarm is never
+                   called */
+                Log.e("** enableAlarmInternal failed " + id + " h " +
+                      enableAlarm.mHour + " m " + enableAlarm.mMinutes);
+                return;
+            }
+
+            long time = calculateAlarm(enableAlarm.mHour, enableAlarm.mMinutes,
+                                       enableAlarm.mDaysOfWeek).getTimeInMillis();
+            values.put(AlarmColumns.ALARM_TIME, time);
+        }
+
+        resolver.update(ContentUris.withAppendedId(AlarmColumns.CONTENT_URI, id),
+                        values, null, null);
+    }
+
+
+    /**
+     * Calculates next scheduled alert
+     */
+    static class AlarmCalculator implements AlarmSettings {
+        public long mMinAlert = Long.MAX_VALUE;
+        public int mMinIdx = -1;
+
+        /**
+         * returns next scheduled alert, MAX_VALUE if none
+         */
+        public long getAlert() {
+            return mMinAlert;
+        }
+        public int getIndex() {
+            return mMinIdx;
+        }
+
+        public void reportAlarm(
+                int idx, boolean enabled, int hour, int minutes,
+                DaysOfWeek daysOfWeek, boolean vibrate, String message,
+                String alert) {
+            if (enabled) {
+                long atTime = calculateAlarm(hour, minutes,
+                                             daysOfWeek).getTimeInMillis();
+                /* Log.i("**  SET ALERT* idx " + idx + " hour " + hour + " minutes " +
+                   minutes + " enabled " + enabled + " calc " + atTime); */
+                if (atTime < mMinAlert) {
+                    mMinIdx = idx;
+                    mMinAlert = atTime;
+                }
+            }
+        }
+    }
+
+    static AlarmCalculator calculateNextAlert(final Context context) {
+        ContentResolver resolver = context.getContentResolver();
+        AlarmCalculator alarmCalc = new AlarmCalculator();
+        getAlarms(resolver, alarmCalc);
+        return alarmCalc;
+    }
+
+    /**
+     * Disables non-repeating alarms that have passed.  Called at
+     * boot.
+     */
+    public static void disableExpiredAlarms(final Context context) {
+        Cursor cur = getAlarmsCursor(context.getContentResolver());
+        long now = System.currentTimeMillis();
+
+        if (cur.moveToFirst()) {
+            do {
+                // Get the field values
+                int id = cur.getInt(AlarmColumns.ALARM_ID_INDEX);
+                boolean enabled = cur.getInt(
+                        AlarmColumns.ALARM_ENABLED_INDEX) == 1 ? true : false;
+                DaysOfWeek daysOfWeek = new DaysOfWeek(
+                        cur.getInt(AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX));
+                long time = cur.getLong(AlarmColumns.ALARM_TIME_INDEX);
+
+                if (enabled && !daysOfWeek.isRepeatSet() && time < now) {
+                    if (Log.LOGV) Log.v(
+                            "** DISABLE " + id + " now " + now +" set " + time);
+                    enableAlarmInternal(context, id, false);
+                }
+            } while (cur.moveToNext());
+        }
+        cur.close();
+    }
+
+    private static NotificationManager getNotificationManager(
+            final Context context) {
+        return (NotificationManager) context.getSystemService(
+                context.NOTIFICATION_SERVICE);
+    }
+
+    /**
+     * Called at system startup, on time/timezone change, and whenever
+     * the user changes alarm settings.  Activates snooze if set,
+     * otherwise loads all alarms, activates next alert.
+     */
+    public static void setNextAlert(final Context context) {
+        int snoozeId = getSnoozeAlarmId(context);
+        if (snoozeId == -1) {
+            AlarmCalculator ac = calculateNextAlert(context);
+            int id = ac.getIndex();
+            long atTime = ac.getAlert();
+
+            if (atTime < Long.MAX_VALUE) {
+                enableAlert(context, id, atTime);
+            } else {
+                disableAlert(context, id);
+            }
+        } else {
+            enableSnoozeAlert(context);
+        }
+    }
+
+    /**
+     * Sets alert in AlarmManger and StatusBar.  This is what will
+     * actually launch the alert when the alarm triggers.
+     *
+     * Note: In general, apps should call setNextAlert() instead of
+     * this method.  setAlert() is only used outside this class when
+     * the alert is not to be driven by the state of the db.  "Snooze"
+     * uses this API, as we do not want to alter the alarm in the db
+     * with each snooze.
+     *
+     * @param id Alarm ID.
+     * @param atTimeInMillis milliseconds since epoch
+     */
+    static void enableAlert(Context context, int id, long atTimeInMillis) {
+        AlarmManager am = (AlarmManager)
+                context.getSystemService(Context.ALARM_SERVICE);
+
+        Intent intent = new Intent(ALARM_ALERT_ACTION);
+        if (Log.LOGV) Log.v("** setAlert id " + id + " atTime " + atTimeInMillis);
+        intent.putExtra(ID, id);
+        intent.putExtra(TIME, atTimeInMillis);
+        PendingIntent sender = PendingIntent.getBroadcast(
+                context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+
+        if (true) {
+            am.set(AlarmManager.RTC_WAKEUP, atTimeInMillis, sender);
+        } else {
+            // a five-second alarm, for testing
+            am.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 5000,
+                   sender);
+        }
+
+        setStatusBarIcon(context, true);
+
+        Calendar c = Calendar.getInstance();
+        c.setTime(new java.util.Date(atTimeInMillis));
+        String timeString = formatDayAndTime(context, c);
+        saveNextAlarm(context, timeString);
+    }
+
+    /**
+     * Disables alert in AlarmManger and StatusBar.
+     *
+     * @param id Alarm ID.
+     */
+    static void disableAlert(Context context, int id) {
+        AlarmManager am = (AlarmManager)
+                context.getSystemService(Context.ALARM_SERVICE);
+        Intent intent = new Intent(ALARM_ALERT_ACTION);
+        intent.putExtra(ID, id);
+        PendingIntent sender = PendingIntent.getBroadcast(
+                context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+        am.cancel(sender);
+        setStatusBarIcon(context, false);
+        saveNextAlarm(context, "");
+    }
+
+    static void saveSnoozeAlert(final Context context, int id,
+                                long atTimeInMillis) {
+        SharedPreferences prefs = context.getSharedPreferences(
+                AlarmClock.PREFERENCES, 0);
+        SharedPreferences.Editor ed = prefs.edit();
+        ed.putInt(PREF_SNOOZE_ID, id);
+        ed.putLong(PREF_SNOOZE_TIME, atTimeInMillis);
+        ed.commit();
+    }
+
+    /**
+     * @return ID of alarm disabled, if disabled, -1 otherwise
+     */
+    static int disableSnoozeAlert(final Context context) {
+        int id = getSnoozeAlarmId(context);
+        if (id == -1) return -1;
+        saveSnoozeAlert(context, -1, 0);
+        return id;
+    }
+
+    /**
+     * @return alarm ID of snoozing alarm, -1 if snooze unset
+     */
+    static int getSnoozeAlarmId(final Context context) {
+        SharedPreferences prefs = context.getSharedPreferences(
+                AlarmClock.PREFERENCES, 0);
+        return prefs.getInt(PREF_SNOOZE_ID, -1);
+    }
+
+    /**
+     * If there is a snooze set, enable it in AlarmManager
+     * @return true if snooze is set
+     */
+    private static boolean enableSnoozeAlert(final Context context) {
+        SharedPreferences prefs = context.getSharedPreferences(
+                AlarmClock.PREFERENCES, 0);
+
+        int id = prefs.getInt(PREF_SNOOZE_ID, -1);
+        if (id == -1) return false;
+        long atTimeInMillis = prefs.getLong(PREF_SNOOZE_TIME, -1);
+        if (id == -1) return false;
+        enableAlert(context, id, atTimeInMillis);
+        return true;
+    }
+
+
+
+    /**
+     * Tells the StatusBar whether the alarm is enabled or disabled
+     */
+    private static void setStatusBarIcon(Context context, boolean enabled) {
+        Intent alarmChanged = new Intent(Intent.ACTION_ALARM_CHANGED);
+        alarmChanged.putExtra("alarmSet", enabled);
+        context.sendBroadcast(alarmChanged);
+    }
+
+    /**
+     * Given an alarm in hours and minutes, return a time suitable for
+     * setting in AlarmManager.
+     * @param hour Always in 24 hour 0-23
+     * @param minute 0-59
+     * @param daysOfWeek 0-59
+     */
+    static Calendar calculateAlarm(int hour, int minute, DaysOfWeek daysOfWeek) {
+
+        // start with now
+        Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(System.currentTimeMillis());
+
+        int nowHour = c.get(Calendar.HOUR_OF_DAY);
+        int nowMinute = c.get(Calendar.MINUTE);
+
+        // if alarm is behind current time, advance one day
+        if (hour < nowHour  ||
+            hour == nowHour && minute <= nowMinute) {
+            c.add(Calendar.DAY_OF_YEAR, 1);
+        }
+        c.set(Calendar.HOUR_OF_DAY, hour);
+        c.set(Calendar.MINUTE, minute);
+        c.set(Calendar.SECOND, 0);
+        c.set(Calendar.MILLISECOND, 0);
+
+        int addDays = daysOfWeek.getNextAlarm(c);
+        /* Log.v("** TIMES * " + c.getTimeInMillis() + " hour " + hour +
+           " minute " + minute + " dow " + c.get(Calendar.DAY_OF_WEEK) + " from now " +
+           addDays); */
+        if (addDays > 0) c.add(Calendar.DAY_OF_WEEK, addDays);
+        return c;
+    }
+
+    static String formatTime(final Context context, int hour, int minute,
+                             DaysOfWeek daysOfWeek) {
+        Calendar c = calculateAlarm(hour, minute, daysOfWeek);
+        return formatTime(context, c);
+    }
+
+    /* used by AlarmAlert */
+    static String formatTime(final Context context, Calendar c) {
+        String format = get24HourMode(context) ? M24 : M12;
+        return (c == null) ? "" : (String)DateFormat.format(format, c);
+    }
+
+    /**
+     * Shows day and time -- used for lock screen
+     */
+    private static String formatDayAndTime(final Context context, Calendar c) {
+        String format = get24HourMode(context) ? DM24 : DM12;
+        return (c == null) ? "" : (String)DateFormat.format(format, c);
+    }
+
+    /**
+     * Save time of the next alarm, as a formatted string, into the system
+     * settings so those who care can make use of it.
+     */
+    static void saveNextAlarm(final Context context, String timeString) {
+        Settings.System.putString(context.getContentResolver(),
+                                  Settings.System.NEXT_ALARM_FORMATTED,
+                                  timeString);
+    }
+
+    /**
+     * @return true if clock is set to 24-hour mode
+     */
+    static boolean get24HourMode(final Context context) {
+        String value = Settings.System.getString(context.getContentResolver(),
+                                                 Settings.System.TIME_12_24);
+        return !(value == null || value.equals("12"));
+    }
+}
diff --git a/src/com/android/alarmclock/ClockPicker.java b/src/com/android/alarmclock/ClockPicker.java
new file mode 100644 (file)
index 0000000..039f5b8
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.widget.AdapterView;
+import android.widget.BaseAdapter;
+import android.widget.Gallery;
+
+/**
+ * Clock face picker for the Alarm Clock application.
+ */
+public class ClockPicker extends Activity implements
+        AdapterView.OnItemSelectedListener, AdapterView.OnItemClickListener {
+
+    private LayoutInflater mFactory;
+    private Gallery mGallery;
+
+    private SharedPreferences mPrefs;
+    private View mClock;
+    private ViewGroup mClockLayout;
+    private int mPosition;
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+        mFactory = LayoutInflater.from(this);
+        setContentView(R.layout.clockpicker);
+
+        mGallery = (Gallery) findViewById(R.id.gallery);
+        mGallery.setAdapter(new ClockAdapter());
+        mGallery.setOnItemSelectedListener(this);
+        mGallery.setOnItemClickListener(this);
+
+        mPrefs = getSharedPreferences(AlarmClock.PREFERENCES, 0);
+        int face = mPrefs.getInt(AlarmClock.PREF_CLOCK_FACE, 0);
+        if (face < 0 || face >= AlarmClock.CLOCKS.length) face = 0;
+
+        mClockLayout = (ViewGroup) findViewById(R.id.clock_layout);
+        mClockLayout.setOnClickListener(new View.OnClickListener() {
+                public void onClick(View v) {
+                    selectClock(mPosition);
+                }
+        });
+
+        mGallery.setSelection(face, false);
+    }
+
+    public void onItemSelected(AdapterView parent, View v, int position, long id) {
+        if (mClock != null) {
+            mClockLayout.removeView(mClock);
+        }
+        mClock = mFactory.inflate(AlarmClock.CLOCKS[position], null);
+        mClockLayout.addView(mClock, 0);
+        mPosition = position;
+    }
+
+    public void onItemClick(AdapterView parent, View v, int position, long id) {
+        selectClock(position);
+    }
+
+    private synchronized void selectClock(int position) {
+        SharedPreferences.Editor ed = mPrefs.edit();
+        ed.putInt("face", position);
+        ed.commit();
+
+        setResult(RESULT_OK);
+        finish();
+    }
+
+    public void onNothingSelected(AdapterView parent) {
+    }
+
+    class ClockAdapter extends BaseAdapter {
+
+        public ClockAdapter() {
+        }
+
+        public int getCount() {
+            return AlarmClock.CLOCKS.length;
+        }
+
+        public Object getItem(int position) {
+            return position;
+        }
+
+        public long getItemId(int position) {
+            return position;
+        }
+
+        public View getView(final int position, View convertView, ViewGroup parent) {
+            View clock = mFactory.inflate(AlarmClock.CLOCKS[position], null);
+            return clock;
+        }
+
+    }
+}
diff --git a/src/com/android/alarmclock/DigitalClock.java b/src/com/android/alarmclock/DigitalClock.java
new file mode 100644 (file)
index 0000000..940d3f0
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.pim.DateFormat;
+import android.provider.Settings;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.Calendar;
+
+/**
+ * Displays the time
+ */
+public class DigitalClock extends LinearLayout {
+
+    private final static String M12 = "h:mm";
+    private final static String M24 = "k:mm";
+
+    private Calendar mCalendar;
+    private String mFormat;
+    private TextView mTimeDisplay;
+    private AmPm mAmPm;
+    private boolean mAnimate;
+    private ContentObserver mFormatChangeObserver;
+    private boolean mLive = true;
+    private boolean mAttached;
+
+    /* called by system on minute ticks */
+    private final Handler mHandler = new Handler();
+    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (mLive && intent.getAction().equals(
+                            Intent.ACTION_TIMEZONE_CHANGED)) {
+                    mCalendar = Calendar.getInstance();
+                }
+                updateTime();
+            }
+        };
+
+    static class AmPm {
+        private int mColorOn, mColorOff;
+
+        private LinearLayout mAmPmLayout;
+        private TextView mAm, mPm;
+
+        AmPm(View parent) {
+            mAmPmLayout = (LinearLayout) parent.findViewById(R.id.am_pm);
+            mAm = (TextView)mAmPmLayout.findViewById(R.id.am);
+            mPm = (TextView)mAmPmLayout.findViewById(R.id.pm);
+
+            Resources r = parent.getResources();
+            mColorOn = r.getColor(R.color.ampm_on);
+            mColorOff = r.getColor(R.color.ampm_off);
+        }
+
+        void setShowAmPm(boolean show) {
+            mAmPmLayout.setVisibility(show ? View.VISIBLE : View.GONE);
+        }
+
+        void setIsMorning(boolean isMorning) {
+            mAm.setTextColor(isMorning ? mColorOn : mColorOff);
+            mPm.setTextColor(isMorning ? mColorOff : mColorOn);
+        }
+    }
+
+    private class FormatChangeObserver extends ContentObserver {
+        public FormatChangeObserver() {
+            super(new Handler());
+        }
+        @Override
+        public void onChange(boolean selfChange) {
+            setDateFormat();
+            updateTime();
+        }
+    }
+
+    public DigitalClock(Context context) {
+        this(context, null);
+    }
+
+    public DigitalClock(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+
+        mTimeDisplay = (TextView) findViewById(R.id.timeDisplay);
+        mAmPm = new AmPm(this);
+        mCalendar = Calendar.getInstance();
+
+        setDateFormat();
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+
+        if (Log.LOGV) Log.v("onAttachedToWindow " + this);
+
+        if (mAttached) return;
+        mAttached = true;
+
+        if (mAnimate) {
+            AnimationDrawable frameAnimation =
+                    (AnimationDrawable) mContext.getResources().getDrawable(
+                            R.drawable.animate_circle);
+            View digitalClock = findViewById(R.id.digitalClock);
+            digitalClock.setBackgroundDrawable(frameAnimation);
+            /* Start the animation (looped playback by default). */
+            ((AnimationDrawable) digitalClock.getBackground()).start();
+            /* No luck wrapping animation or individual bitmaps in a
+               ScaleDrawable */
+            digitalClock.requestLayout();
+        }
+
+        if (mLive) {
+            /* monitor time ticks, time changed, timezone */
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_TIME_TICK);
+            filter.addAction(Intent.ACTION_TIME_CHANGED);
+            filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
+            mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+        }
+
+        /* monitor 12/24-hour display preference */
+        mFormatChangeObserver = new FormatChangeObserver();
+        mContext.getContentResolver().registerContentObserver(
+                Settings.System.CONTENT_URI, true, mFormatChangeObserver);
+
+        updateTime();
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        if (!mAttached) return;
+        mAttached = false;
+
+        Drawable background = getBackground();
+        if (background instanceof AnimationDrawable) {
+            ((AnimationDrawable) background).stop();
+        }
+
+        if (mLive) {
+            mContext.unregisterReceiver(mIntentReceiver);
+        }
+        mContext.getContentResolver().unregisterContentObserver(
+                mFormatChangeObserver);
+    }
+
+
+    void updateTime(Calendar c) {
+        mCalendar = c;
+        updateTime();
+    }
+
+    private void updateTime() {
+        if (mLive) {
+            mCalendar.setTimeInMillis(System.currentTimeMillis());
+        }
+
+        CharSequence newTime = DateFormat.format(mFormat, mCalendar);
+        mTimeDisplay.setText(newTime);
+        mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0);
+    }
+
+    private void setDateFormat() {
+        mFormat = Alarms.get24HourMode(mContext) ? M24 : M12;
+        mAmPm.setShowAmPm(mFormat == M12);
+    }
+
+    void setAnimate() {
+        mAnimate = true;
+    }
+
+    void setLive(boolean live) {
+        mLive = live;
+    }
+}
diff --git a/src/com/android/alarmclock/Log.java b/src/com/android/alarmclock/Log.java
new file mode 100644 (file)
index 0000000..732e3e7
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2008 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-level logging flag
+ */
+
+package com.android.alarmclock;
+
+import android.os.SystemClock;
+import android.util.Config;
+
+class Log {
+    public final static String LOGTAG = "AlarmClock";
+
+    private static final boolean DEBUG = false;
+    static final boolean LOGV = DEBUG ? Config.LOGD : Config.LOGV;
+
+    static void v(String logMe) {
+        android.util.Log.v(LOGTAG, /* SystemClock.uptimeMillis() + " " + */ logMe);
+    }
+
+    static void e(String logMe) {
+        android.util.Log.e(LOGTAG, logMe);
+    }
+}
diff --git a/src/com/android/alarmclock/RepeatPreference.java b/src/com/android/alarmclock/RepeatPreference.java
new file mode 100644 (file)
index 0000000..a8c60ac
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.app.AlertDialog.Builder;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.preference.ListPreference;
+import android.util.AttributeSet;
+
+public class RepeatPreference extends ListPreference {
+    private Alarms.DaysOfWeek mDaysOfWeek = new Alarms.DaysOfWeek();
+    private OnRepeatChangeListener mOnRepeatChangeListener;
+
+    public interface OnRepeatChangeListener {
+        /** Called when this preference has changed */
+        public void onRepeatChanged(Alarms.DaysOfWeek daysOfWeek);
+    }
+
+    public RepeatPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    void setDaysOfWeek(Alarms.DaysOfWeek daysOfWeek) {
+        /* we keep a local copy, so the host can set itself on a
+           positive result and ignore on a negative */
+        mDaysOfWeek.set(daysOfWeek);
+    }
+
+    void setOnRepeatChangeListener(OnRepeatChangeListener listener) {
+        mOnRepeatChangeListener = listener;
+    }
+
+    @Override
+    protected void onDialogClosed(boolean positiveResult) {
+        if (positiveResult) {
+            mOnRepeatChangeListener.onRepeatChanged(mDaysOfWeek);
+        }
+    }
+
+    @Override
+    protected void onPrepareDialogBuilder(Builder builder) {
+        CharSequence[] entries = getEntries();
+        CharSequence[] entryValues = getEntryValues();
+
+        if (entries == null || entryValues == null) {
+            throw new IllegalStateException(
+                    "RepeatPreference requires an entries array and an entryValues array.");
+        }
+        builder.setMultiChoiceItems(
+                entries, mDaysOfWeek.getBooleanArray(),
+                new DialogInterface.OnMultiChoiceClickListener() {
+                    public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+                        mDaysOfWeek.set(which, isChecked);
+                    }
+                });
+    }
+}
diff --git a/src/com/android/alarmclock/SetAlarm.java b/src/com/android/alarmclock/SetAlarm.java
new file mode 100644 (file)
index 0000000..3f40fcf
--- /dev/null
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.app.TimePickerDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.pim.DateFormat;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.preference.CheckBoxPreference;
+import android.preference.PreferenceScreen;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.TimePicker;
+import android.widget.Toast;
+
+/**
+ * Manages each alarm
+ */
+public class SetAlarm extends PreferenceActivity
+        implements Alarms.AlarmSettings, TimePickerDialog.OnTimeSetListener {
+
+    private CheckBoxPreference mAlarmOnPref;
+    private Preference mTimePref;
+    private AlarmPreference mAlarmPref;
+    private CheckBoxPreference mVibratePref;
+    private RepeatPreference mRepeatPref;
+    private ContentObserver mAlarmsChangeObserver;
+    private MenuItem mDeleteAlarmItem;
+
+    private int mId;
+    private int mHour;
+    private int mMinutes;
+    private Alarms.DaysOfWeek mDaysOfWeek = new Alarms.DaysOfWeek();
+
+    private boolean mReportAlarmCalled;
+
+    private static final int DIALOG_TIMEPICKER = 0;
+
+    private class RingtoneChangedListener implements AlarmPreference.IRingtoneChangedListener {
+        public void onRingtoneChanged(Uri ringtoneUri) {
+            saveAlarm(false);
+        }
+    }
+
+    private class OnRepeatChangeListener implements RepeatPreference.OnRepeatChangeListener {
+        public void onRepeatChanged(Alarms.DaysOfWeek daysOfWeek) {
+            if (!mDaysOfWeek.equals(daysOfWeek)) {
+                mDaysOfWeek.set(daysOfWeek);
+                saveAlarm(true);
+            }
+        }
+    }
+
+    private class AlarmsChangeObserver extends ContentObserver {
+        public AlarmsChangeObserver() {
+            super(new Handler());
+        }
+        @Override
+        public void onChange(boolean selfChange) {
+            Alarms.getAlarm(getContentResolver(), SetAlarm.this, mId);
+        }
+    }
+
+    /**
+     * Set an alarm.  Requires an Alarms.ID to be passed in as an
+     * extra
+     */
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+
+        addPreferencesFromResource(R.xml.alarm_prefs);
+        mAlarmOnPref = (CheckBoxPreference)findPreference("on");
+        mTimePref = findPreference("time");
+        mAlarmPref = (AlarmPreference) findPreference("alarm");
+        mVibratePref = (CheckBoxPreference) findPreference("vibrate");
+        mRepeatPref = (RepeatPreference) findPreference("setRepeat");
+
+        Intent i = getIntent();
+        mId = i.getIntExtra(Alarms.ID, -1);
+        if (Log.LOGV) Log.v("In SetAlarm, alarm id = " + mId);
+
+        mReportAlarmCalled = false;
+        /* load alarm details from database */
+        Alarms.getAlarm(getContentResolver(), this, mId);
+        /* This should never happen, but does occasionally with the monkey.
+         * I believe it's a race condition where a deleted alarm is opened
+         * before the alarm list is refreshed. */
+        if (!mReportAlarmCalled) {
+            Log.e("reportAlarm never called!");
+            finish();
+        }
+
+        mAlarmsChangeObserver = new AlarmsChangeObserver();
+        getContentResolver().registerContentObserver(
+                Alarms.AlarmColumns.CONTENT_URI, true, mAlarmsChangeObserver);
+
+        mAlarmPref.setRingtoneChangedListener(new RingtoneChangedListener());
+        mRepeatPref.setOnRepeatChangeListener(new OnRepeatChangeListener());
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        getContentResolver().unregisterContentObserver(mAlarmsChangeObserver);
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        Dialog d;
+
+        switch (id) {
+        case DIALOG_TIMEPICKER:
+            d = new TimePickerDialog(
+                    SetAlarm.this,
+                    this,
+                    0,
+                    0,
+                    DateFormat.is24HourFormat(SetAlarm.this));
+            d.setTitle(getResources().getString(R.string.time));
+            break;
+        default:
+            d = null;
+        }
+
+        return d;
+    }
+
+    @Override
+    protected void onPrepareDialog(int id, Dialog dialog) {
+        super.onPrepareDialog(id, dialog);
+
+        switch (id) {
+        case DIALOG_TIMEPICKER:
+            TimePickerDialog timePicker = (TimePickerDialog)dialog;
+            timePicker.updateTime(mHour, mMinutes);
+            break;
+        }
+    }
+
+    @Override
+    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
+
+        if (preference == mTimePref) {
+            showDialog(DIALOG_TIMEPICKER);
+        } else if (preference == mAlarmOnPref) {
+            saveAlarm(true);
+        } else if (preference == mVibratePref) {
+            saveAlarm(false);
+        }
+
+        return super.onPreferenceTreeClick(preferenceScreen, preference);
+    }
+
+    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+        mHour = hourOfDay;
+        mMinutes = minute;
+        mAlarmOnPref.setChecked(true);
+        saveAlarm(true);
+    }
+
+    /**
+     * Alarms.AlarmSettings implementation.  Database feeds current
+     * settings in through this call
+     */
+    public void reportAlarm(
+            int idx, boolean enabled, int hour, int minutes,
+            Alarms.DaysOfWeek daysOfWeek, boolean vibrate,String message,
+            String alert) {
+
+        mHour = hour;
+        mMinutes = minutes;
+        mAlarmOnPref.setChecked(enabled);
+        mDaysOfWeek.set(daysOfWeek);
+        mRepeatPref.setDaysOfWeek(mDaysOfWeek);
+        mVibratePref.setChecked(vibrate);
+
+        if (alert == null || alert.length() == 0) {
+            if (Log.LOGV) Log.v("****** reportAlarm null or 0-length alert");
+            mAlarmPref.mAlert = getDefaultAlarm();
+            if (mAlarmPref.mAlert == null) {
+                Log.e("****** Default Alarm null");
+            }
+        } else {
+            mAlarmPref.mAlert = Uri.parse(alert);
+            if (mAlarmPref.mAlert == null) {
+                Log.e("****** Parsed null alarm. URI: " + alert);
+            }
+        }
+        if (Log.LOGV) Log.v("****** reportAlarm uri " + alert + " alert " +
+                            mAlarmPref.mAlert);
+        updateTime();
+        updateRepeat();
+        updateAlarm(mAlarmPref.mAlert);
+
+        mReportAlarmCalled = true;
+    }
+
+    /**
+     * picks the first alarm available
+     */
+    private Uri getDefaultAlarm() {
+        RingtoneManager ringtoneManager = new RingtoneManager(this);
+        ringtoneManager.setType(RingtoneManager.TYPE_ALARM);
+        return ringtoneManager.getRingtoneUri(0);
+    }
+
+    private void updateTime() {
+        if (Log.LOGV) Log.v("updateTime " + mId);
+        mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinutes, mDaysOfWeek));
+    }
+
+    private void updateAlarm(Uri ringtoneUri) {
+        if (Log.LOGV) Log.v("updateAlarm " + mId);
+        Ringtone ringtone = RingtoneManager.getRingtone(SetAlarm.this, ringtoneUri);
+        if (ringtone != null) {
+            mAlarmPref.setSummary(ringtone.getTitle(SetAlarm.this));
+        }
+    }
+
+    private void updateRepeat() {
+        if (Log.LOGV) Log.v("updateRepeat " + mId);
+        mRepeatPref.setSummary(mDaysOfWeek.toString(this, true));
+    }
+
+    private void saveAlarm(boolean popToast) {
+        if (mReportAlarmCalled && mAlarmPref.mAlert != null) {
+            String alertString = mAlarmPref.mAlert.toString();
+            saveAlarm(this, mId, mAlarmOnPref.isChecked(), mHour, mMinutes,
+                      mDaysOfWeek, mVibratePref.isChecked(), alertString,
+                      popToast);
+        }
+    }
+
+    /**
+     * Write alarm out to persistent store and pops toast if alarm
+     * enabled
+     */
+    private static void saveAlarm(
+            Context context, int id, boolean enabled, int hour, int minute,
+            Alarms.DaysOfWeek daysOfWeek, boolean vibrate, String alert,
+            boolean popToast) {
+        if (Log.LOGV) Log.v("** saveAlarm " + id + " " + enabled + " " + hour +
+                            " " + minute + " vibe " + vibrate);
+
+        // Fix alert string first
+        Alarms.setAlarm(context, id, enabled, hour, minute, daysOfWeek, vibrate,
+                        "", alert);
+
+        if (enabled && popToast) {
+            popAlarmSetToast(context, hour, minute, daysOfWeek);
+        }
+    }
+
+    /**
+     * Display a toast that tells the user how long until the alarm
+     * goes off.  This helps prevent "am/pm" mistakes.
+     */
+    static void popAlarmSetToast(Context context, int hour, int minute,
+                                 Alarms.DaysOfWeek daysOfWeek) {
+
+        String toastText = formatToast(context, hour, minute, daysOfWeek);
+        Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG);
+        ToastMaster.setToast(toast);
+        toast.show();
+    }
+
+    /**
+     * format "Alarm set for 2 days 7 hours and 53 minutes from
+     * now"
+     */
+    static String formatToast(Context context, int hour, int minute,
+                              Alarms.DaysOfWeek daysOfWeek) {
+        long alarm = Alarms.calculateAlarm(hour, minute,
+                                           daysOfWeek).getTimeInMillis();
+        long delta = alarm - System.currentTimeMillis();;
+        long hours = delta / (1000 * 60 * 60);
+        long minutes = delta / (1000 * 60) % 60;
+        long days = hours / 24;
+        hours = hours % 24;
+
+        String daySeq = (days == 0) ? "" :
+                (days == 1) ? context.getString(R.string.day) :
+                context.getString(R.string.days, Long.toString(days));
+
+        String minSeq = (minutes == 0) ? "" :
+                (minutes == 1) ? context.getString(R.string.minute) :
+                context.getString(R.string.minutes, Long.toString(minutes));
+
+        String hourSeq = (hours == 0) ? "" :
+                (hours == 1) ? context.getString(R.string.hour) :
+                context.getString(R.string.hours, Long.toString(hours));
+
+        boolean dispDays = days > 0;
+        boolean dispHour = hours > 0;
+        boolean dispMinute = minutes > 0;
+
+        String ret;
+        if (!(dispDays || dispHour || dispMinute)) {
+            ret = context.getString(R.string.subminute);
+        } else {
+            String parts[] = new String[5];
+            parts[0] = daySeq;
+            parts[1] = !dispDays ? "" :
+                    dispHour && dispMinute ? context.getString(R.string.space) :
+                    !dispHour && !dispMinute ? "" :
+                    context.getString(R.string.and);
+            parts[2] = dispHour ? hourSeq : "";
+            parts[3] = dispHour && dispMinute ? context.getString(R.string.and) : "";
+            parts[4] = dispMinute ? minSeq : "";
+            ret = context.getString(R.string.combiner, (Object[])parts);
+        }
+
+        ret = context.getString(R.string.alarm_set, ret);
+        /* if (Log.LOGV) Log.v("** TOAST daySeq " + daySeq + " hourSeq " + hourSeq +
+           " minSeq " + minSeq + " ret " + ret); */
+        return ret;
+    }
+
+    public boolean onCreateOptionsMenu(Menu menu) {
+        super.onCreateOptionsMenu(menu);
+
+        mDeleteAlarmItem = menu.add(0, 0, 0, R.string.delete_alarm);
+        mDeleteAlarmItem.setIcon(android.R.drawable.ic_menu_delete);
+
+        return true;
+    }
+
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item == mDeleteAlarmItem) {
+
+            /* If alarm is snoozing, lose it */
+            int id = Alarms.getSnoozeAlarmId(this);
+            if (id == mId) Alarms.disableSnoozeAlert(this);
+
+            Alarms.deleteAlarm(getContentResolver(), mId);
+            Alarms.setNextAlert(this);
+            finish();
+            return true;
+        }
+
+        return false;
+    }
+
+}
diff --git a/src/com/android/alarmclock/ToastMaster.java b/src/com/android/alarmclock/ToastMaster.java
new file mode 100644 (file)
index 0000000..c4c2815
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.alarmclock;
+
+import android.widget.Toast;
+
+public class ToastMaster {
+
+    private static Toast sToast = null;
+
+    private ToastMaster() {
+
+    }
+
+    public static void setToast(Toast toast) {
+        if (sToast != null)
+            sToast.cancel();
+        sToast = toast;
+    }
+
+    public static void cancelToast() {
+        if (sToast != null)
+            sToast.cancel();
+        sToast = null;
+    }
+
+}