--- /dev/null
+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)
--- /dev/null
+<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>
+
--- /dev/null
+
+ 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
+
--- /dev/null
+<?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>
+
+
+
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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"/>
--- /dev/null
+<?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"/>
--- /dev/null
+<?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"/>
--- /dev/null
+<?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"/>
--- /dev/null
+<?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"/>
--- /dev/null
+<?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>
+
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+<?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- & 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>
--- /dev/null
+<?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 & 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>
--- /dev/null
+<?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>
--- /dev/null
+<?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 & 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>
+
+
--- /dev/null
+<?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>
--- /dev/null
+<?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>
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
--- /dev/null
+/*
+ * 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();
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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"));
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ });
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}