2 * Copyright (C) 2007 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.alarmclock;
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.Intent;
24 import android.content.SharedPreferences;
25 import android.database.Cursor;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.SystemProperties;
30 import android.provider.Settings;
31 import android.view.ContextMenu;
32 import android.view.ContextMenu.ContextMenuInfo;
33 import android.view.LayoutInflater;
34 import android.view.Menu;
35 import android.view.MenuItem;
36 import android.view.View;
37 import android.view.View.OnClickListener;
38 import android.view.ViewGroup;
39 import android.widget.CursorAdapter;
40 import android.widget.ListView;
41 import android.widget.TextView;
42 import android.widget.CheckBox;
43 import android.media.AudioManager;
44 import java.util.Calendar;
45 import java.text.DateFormatSymbols;
48 * AlarmClock application.
50 public class AlarmClock extends Activity {
52 final static String PREFERENCES = "AlarmClock";
53 final static String PREF_CLOCK_FACE = "face";
54 final static String PREF_SHOW_CLOCK = "show_clock";
56 /** Cap alarm count at this number */
57 final static int MAX_ALARM_COUNT = 12;
59 /** This must be false for production. If true, turns on logging,
61 final static boolean DEBUG = false;
63 private SharedPreferences mPrefs;
64 private LayoutInflater mFactory;
65 private ViewGroup mClockLayout;
66 private View mClock = null;
67 private MenuItem mAddAlarmItem;
68 private MenuItem mToggleClockItem;
69 private ListView mAlarmsList;
70 private Cursor mCursor;
71 private static boolean volumeAdjustable;
72 private String mAm, mPm;
75 * Which clock face to show
77 private int mFace = -1;
80 * FIXME: it would be nice for this to live in an xml config file.
82 final static int[] CLOCKS = {
83 R.layout.clock_basic_bw,
84 R.layout.clock_googly,
85 R.layout.clock_droid2,
86 R.layout.clock_droids,
87 R.layout.digital_clock
90 private class AlarmTimeAdapter extends CursorAdapter {
91 public AlarmTimeAdapter(Context context, Cursor cursor) {
92 super(context, cursor);
95 public View newView(Context context, Cursor cursor, ViewGroup parent) {
96 View ret = mFactory.inflate(R.layout.alarm_time, parent, false);
98 ((TextView) ret.findViewById(R.id.am)).setText(mAm);
99 ((TextView) ret.findViewById(R.id.pm)).setText(mPm);
101 DigitalClock digitalClock = (DigitalClock)ret.findViewById(R.id.digitalClock);
102 digitalClock.setLive(false);
103 if (Log.LOGV) Log.v("newView " + cursor.getPosition());
107 public void bindView(View view, Context context, Cursor cursor) {
108 final int id = cursor.getInt(Alarms.AlarmColumns.ALARM_ID_INDEX);
109 final int hour = cursor.getInt(Alarms.AlarmColumns.ALARM_HOUR_INDEX);
110 final int minutes = cursor.getInt(Alarms.AlarmColumns.ALARM_MINUTES_INDEX);
111 final Alarms.DaysOfWeek daysOfWeek = new Alarms.DaysOfWeek(
112 cursor.getInt(Alarms.AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX));
113 final boolean enabled = cursor.getInt(Alarms.AlarmColumns.ALARM_ENABLED_INDEX) == 1;
115 cursor.getString(Alarms.AlarmColumns.ALARM_MESSAGE_INDEX);
117 CheckBox onButton = (CheckBox)view.findViewById(R.id.alarmButton);
118 onButton.setChecked(enabled);
119 onButton.setOnClickListener(new OnClickListener() {
120 public void onClick(View v) {
121 boolean isChecked = ((CheckBox) v).isChecked();
122 Alarms.enableAlarm(AlarmClock.this, id, isChecked);
124 SetAlarm.popAlarmSetToast(
125 AlarmClock.this, hour, minutes, daysOfWeek);
130 DigitalClock digitalClock = (DigitalClock)view.findViewById(R.id.digitalClock);
131 if (Log.LOGV) Log.v("bindView " + cursor.getPosition() + " " + id + " " + hour +
132 ":" + minutes + " " + daysOfWeek.toString(context, true) + " dc " + digitalClock);
134 digitalClock.setOnClickListener(new OnClickListener() {
135 public void onClick(View v) {
137 Intent intent = new Intent(AlarmClock.this, SetAlarm.class);
138 intent.putExtra(Alarms.ID, id);
139 startActivity(intent);
141 // TESTING: immediately pop alarm
142 Intent fireAlarm = new Intent(AlarmClock.this, AlarmAlert.class);
143 fireAlarm.putExtra(Alarms.ID, id);
144 fireAlarm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
145 startActivity(fireAlarm);
150 // set the alarm text
151 final Calendar c = Calendar.getInstance();
152 c.set(Calendar.HOUR_OF_DAY, hour);
153 c.set(Calendar.MINUTE, minutes);
154 digitalClock.updateTime(c);
156 // Set the repeat text or leave it blank if it does not repeat.
157 TextView daysOfWeekView = (TextView) digitalClock.findViewById(R.id.daysOfWeek);
158 final String daysOfWeekStr =
159 daysOfWeek.toString(AlarmClock.this, false);
160 if (daysOfWeekStr != null && daysOfWeekStr.length() != 0) {
161 daysOfWeekView.setText(daysOfWeekStr);
162 daysOfWeekView.setVisibility(View.VISIBLE);
164 daysOfWeekView.setVisibility(View.GONE);
169 (TextView) digitalClock.findViewById(R.id.label);
170 if (label != null && label.length() != 0) {
171 labelView.setText(label);
173 labelView.setText(R.string.default_label);
176 // Build context menu
177 digitalClock.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
178 public void onCreateContextMenu(ContextMenu menu, View view,
179 ContextMenuInfo menuInfo) {
180 menu.setHeaderTitle(Alarms.formatTime(AlarmClock.this, c));
181 MenuItem deleteAlarmItem = menu.add(0, id, 0, R.string.delete_alarm);
188 public boolean onContextItemSelected(final MenuItem item) {
189 // Confirm that the alarm will be deleted.
190 new AlertDialog.Builder(this)
191 .setTitle(getString(R.string.delete_alarm))
192 .setMessage(getString(R.string.delete_alarm_confirm))
193 .setPositiveButton(android.R.string.ok,
194 new DialogInterface.OnClickListener() {
195 public void onClick(DialogInterface d, int w) {
196 Alarms.deleteAlarm(AlarmClock.this,
200 .setNegativeButton(android.R.string.cancel, null)
206 protected void onCreate(Bundle icicle) {
207 super.onCreate(icicle);
208 volumeAdjustable=SystemProperties.getBoolean("ro.alarm.volume.adjustable",false);
209 AlarmClock.setVolumeControlForPlatform(this);
211 String[] ampm = new DateFormatSymbols().getAmPmStrings();
215 // sanity check -- no database, no clock
216 if (getContentResolver() == null) {
217 new AlertDialog.Builder(this)
218 .setTitle(getString(R.string.error))
219 .setMessage(getString(R.string.dberror))
222 new DialogInterface.OnClickListener() {
223 public void onClick(DialogInterface dialog, int which) {
227 .setOnCancelListener(
228 new DialogInterface.OnCancelListener() {
229 public void onCancel(DialogInterface dialog) {
232 .setIcon(android.R.drawable.ic_dialog_alert)
237 setContentView(R.layout.alarm_clock);
238 mFactory = LayoutInflater.from(this);
239 mPrefs = getSharedPreferences(PREFERENCES, 0);
241 mCursor = Alarms.getAlarmsCursor(getContentResolver());
242 mAlarmsList = (ListView) findViewById(R.id.alarms_list);
243 mAlarmsList.setAdapter(new AlarmTimeAdapter(this, mCursor));
244 mAlarmsList.setVerticalScrollBarEnabled(true);
245 mAlarmsList.setItemsCanFocus(true);
247 mClockLayout = (ViewGroup) findViewById(R.id.clock_layout);
248 mClockLayout.setOnClickListener(new View.OnClickListener() {
249 public void onClick(View v) {
250 final Intent intent = new Intent(AlarmClock.this, ClockPicker.class);
251 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
252 startActivity(intent);
256 setClockVisibility(mPrefs.getBoolean(PREF_SHOW_CLOCK, true));
260 protected void onResume() {
263 int face = mPrefs.getInt(PREF_CLOCK_FACE, 0);
265 if (face < 0 || face >= AlarmClock.CLOCKS.length)
274 protected void onDestroy() {
276 ToastMaster.cancelToast();
277 mCursor.deactivate();
280 protected void inflateClock() {
281 if (mClock != null) {
282 mClockLayout.removeView(mClock);
285 LayoutInflater.from(this).inflate(CLOCKS[mFace], mClockLayout);
286 mClock = findViewById(R.id.clock);
288 TextView am = (TextView) findViewById(R.id.am);
289 TextView pm = (TextView) findViewById(R.id.pm);
300 public boolean onCreateOptionsMenu(Menu menu) {
302 getMenuInflater().inflate(R.menu.main_menu, menu);
304 return super.onCreateOptionsMenu(menu);
308 * Only allow user to add a new alarm if there are fewer than
312 public boolean onPrepareOptionsMenu(Menu menu) {
313 menu.findItem(R.id.menu_add_alarm).setVisible(
314 mAlarmsList.getAdapter().getCount() < MAX_ALARM_COUNT);
315 menu.findItem(R.id.menu_toggle_clock).setTitle(
316 getClockVisibility() ? R.string.hide_clock
317 : R.string.show_clock);
318 return super.onPrepareOptionsMenu(menu);
322 public boolean onOptionsItemSelected(MenuItem item) {
323 switch (item.getItemId()) {
324 case R.id.menu_add_alarm:
325 Uri uri = Alarms.addAlarm(getContentResolver());
326 // FIXME: scroll to new item?
327 String segment = uri.getPathSegments().get(1);
328 int newId = Integer.parseInt(segment);
330 Log.v("In AlarmClock, new alarm id = " + newId);
332 Intent intent = new Intent(this, SetAlarm.class);
333 intent.putExtra(Alarms.ID, newId);
334 startActivity(intent);
337 case R.id.menu_toggle_clock:
338 setClockVisibility(!getClockVisibility());
339 saveClockVisibility();
342 case R.id.menu_settings:
343 startActivity(new Intent(this, SettingsActivity.class));
347 return super.onOptionsItemSelected(item);
351 private boolean getClockVisibility() {
352 return mClockLayout.getVisibility() == View.VISIBLE;
355 private void setClockVisibility(boolean visible) {
356 mClockLayout.setVisibility(visible ? View.VISIBLE : View.GONE);
359 private void saveClockVisibility() {
360 mPrefs.edit().putBoolean(PREF_SHOW_CLOCK, getClockVisibility()).commit();
362 public static void setVolumeControlForPlatform(Activity context){
363 if (isVolumeAdjustable()){
364 context.setVolumeControlStream(AudioManager.STREAM_ALARM);
366 //Use default stream type,the default type use a fixed volume
367 context.setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
370 public static boolean isVolumeAdjustable(){
371 return volumeAdjustable;