AlarmClock.java revision 57c4e8b2ef600f89caafb0520b44b06fc73ab845
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
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
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package com.android.alarmclock;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.Intent;
24import android.content.SharedPreferences;
25import android.database.Cursor;
26import android.net.Uri;
27import android.os.Bundle;
28import android.os.Handler;
29import android.provider.Settings;
30import android.view.ContextMenu;
31import android.view.ContextMenu.ContextMenuInfo;
32import android.view.LayoutInflater;
33import android.view.Menu;
34import android.view.MenuItem;
35import android.view.View;
36import android.view.View.OnClickListener;
37import android.view.ViewGroup;
38import android.widget.CursorAdapter;
39import android.widget.ListView;
40import android.widget.TextView;
41import android.widget.CheckBox;
42
43import java.util.Calendar;
44
45/**
46 * AlarmClock application.
47 */
48public class AlarmClock extends Activity {
49
50    final static String PREFERENCES = "AlarmClock";
51    final static int SET_ALARM = 1;
52    final static String PREF_CLOCK_FACE = "face";
53    final static String PREF_SHOW_CLOCK = "show_clock";
54
55    /** Cap alarm count at this number */
56    final static int MAX_ALARM_COUNT = 12;
57
58    /** This must be false for production.  If true, turns on logging,
59        test code, etc. */
60    final static boolean DEBUG = false;
61
62    private SharedPreferences mPrefs;
63    private LayoutInflater mFactory;
64    private ViewGroup mClockLayout;
65    private View mClock = null;
66    private MenuItem mAddAlarmItem;
67    private MenuItem mToggleClockItem;
68    private ListView mAlarmsList;
69    private Cursor mCursor;
70
71    /**
72     * Which clock face to show
73     */
74    private int mFace = -1;
75
76    /*
77     * FIXME: it would be nice for this to live in an xml config file.
78     */
79    final static int[] CLOCKS = {
80        R.layout.clock_basic_bw,
81        R.layout.clock_googly,
82        R.layout.clock_droid2,
83        R.layout.clock_droids,
84        R.layout.digital_clock
85    };
86
87    private class AlarmTimeAdapter extends CursorAdapter {
88        public AlarmTimeAdapter(Context context, Cursor cursor) {
89            super(context, cursor);
90        }
91
92        public View newView(Context context, Cursor cursor, ViewGroup parent) {
93            View ret = mFactory.inflate(R.layout.alarm_time, parent, false);
94            DigitalClock digitalClock = (DigitalClock)ret.findViewById(R.id.digitalClock);
95            digitalClock.setLive(false);
96            if (Log.LOGV) Log.v("newView " + cursor.getPosition());
97            return ret;
98        }
99
100        public void bindView(View view, Context context, Cursor cursor) {
101            final int id = cursor.getInt(Alarms.AlarmColumns.ALARM_ID_INDEX);
102            final int hour = cursor.getInt(Alarms.AlarmColumns.ALARM_HOUR_INDEX);
103            final int minutes = cursor.getInt(Alarms.AlarmColumns.ALARM_MINUTES_INDEX);
104            final Alarms.DaysOfWeek daysOfWeek = new Alarms.DaysOfWeek(
105                    cursor.getInt(Alarms.AlarmColumns.ALARM_DAYS_OF_WEEK_INDEX));
106            final boolean enabled = cursor.getInt(Alarms.AlarmColumns.ALARM_ENABLED_INDEX) == 1;
107            final String label =
108                    cursor.getString(Alarms.AlarmColumns.ALARM_MESSAGE_INDEX);
109
110            CheckBox onButton = (CheckBox)view.findViewById(R.id.alarmButton);
111            onButton.setChecked(enabled);
112            onButton.setOnClickListener(new OnClickListener() {
113                    public void onClick(View v) {
114                        boolean isChecked = ((CheckBox) v).isChecked();
115                        Alarms.enableAlarm(AlarmClock.this, id, isChecked);
116                        if (isChecked) {
117                            SetAlarm.popAlarmSetToast(
118                                    AlarmClock.this, hour, minutes, daysOfWeek);
119                        }
120                    }
121            });
122
123            DigitalClock digitalClock = (DigitalClock)view.findViewById(R.id.digitalClock);
124            if (Log.LOGV) Log.v("bindView " + cursor.getPosition() + " " + id + " " + hour +
125                                ":" + minutes + " " + daysOfWeek.toString(context, true) + " dc " + digitalClock);
126
127            digitalClock.setOnClickListener(new OnClickListener() {
128                    public void onClick(View v) {
129                        if (true) {
130                            Intent intent = new Intent(AlarmClock.this, SetAlarm.class);
131                            intent.putExtra(Alarms.ID, id);
132                            startActivityForResult(intent, SET_ALARM);
133                        } else {
134                            // TESTING: immediately pop alarm
135                            Intent fireAlarm = new Intent(AlarmClock.this, AlarmAlert.class);
136                            fireAlarm.putExtra(Alarms.ID, id);
137                            fireAlarm.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
138                            startActivity(fireAlarm);
139                        }
140                    }
141                });
142
143            // set the alarm text
144            final Calendar c = Calendar.getInstance();
145            c.set(Calendar.HOUR_OF_DAY, hour);
146            c.set(Calendar.MINUTE, minutes);
147            digitalClock.updateTime(c);
148
149            // Set the repeat text or leave it blank if it does not repeat.
150            TextView daysOfWeekView = (TextView) digitalClock.findViewById(R.id.daysOfWeek);
151            final String daysOfWeekStr =
152                    daysOfWeek.toString(AlarmClock.this, false);
153            if (daysOfWeekStr != null && daysOfWeekStr.length() != 0) {
154                daysOfWeekView.setText(daysOfWeekStr);
155                daysOfWeekView.setVisibility(View.VISIBLE);
156            } else {
157                daysOfWeekView.setVisibility(View.GONE);
158            }
159
160            // Display the label
161            TextView labelView =
162                    (TextView) digitalClock.findViewById(R.id.label);
163            if (label != null && label.length() != 0) {
164                labelView.setText(label);
165            } else {
166                labelView.setText(R.string.default_label);
167            }
168
169            // Build context menu
170            digitalClock.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() {
171                    public void onCreateContextMenu(ContextMenu menu, View view,
172                                                    ContextMenuInfo menuInfo) {
173                        menu.setHeaderTitle(Alarms.formatTime(AlarmClock.this, c));
174                        MenuItem deleteAlarmItem = menu.add(0, id, 0, R.string.delete_alarm);
175                    }
176                });
177        }
178    };
179
180    @Override
181    public boolean onContextItemSelected(final MenuItem item) {
182        // Confirm that the alarm will be deleted.
183        new AlertDialog.Builder(this)
184                .setTitle(getString(R.string.delete_alarm))
185                .setMessage(getString(R.string.delete_alarm_confirm))
186                .setPositiveButton(android.R.string.ok,
187                        new DialogInterface.OnClickListener() {
188                            public void onClick(DialogInterface d, int w) {
189                                Alarms.deleteAlarm(AlarmClock.this,
190                                        item.getItemId());
191                            }
192                        })
193                .setNegativeButton(android.R.string.cancel, null)
194                .show();
195        return true;
196    }
197
198    @Override
199    protected void onCreate(Bundle icicle) {
200        super.onCreate(icicle);
201
202        // sanity check -- no database, no clock
203        if (getContentResolver() == null) {
204            new AlertDialog.Builder(this)
205                    .setTitle(getString(R.string.error))
206                    .setMessage(getString(R.string.dberror))
207                    .setPositiveButton(
208                            android.R.string.ok,
209                            new DialogInterface.OnClickListener() {
210                                public void onClick(DialogInterface dialog, int which) {
211                                    finish();
212                                }
213                            })
214                    .setOnCancelListener(
215                            new DialogInterface.OnCancelListener() {
216                                public void onCancel(DialogInterface dialog) {
217                                    finish();
218                                }})
219                    .setIcon(android.R.drawable.ic_dialog_alert)
220                    .create().show();
221            return;
222        }
223
224        setContentView(R.layout.alarm_clock);
225        mFactory = LayoutInflater.from(this);
226        mPrefs = getSharedPreferences(PREFERENCES, 0);
227
228        mCursor = Alarms.getAlarmsCursor(getContentResolver());
229        mAlarmsList = (ListView) findViewById(R.id.alarms_list);
230        mAlarmsList.setAdapter(new AlarmTimeAdapter(this, mCursor));
231        mAlarmsList.setVerticalScrollBarEnabled(true);
232        mAlarmsList.setItemsCanFocus(true);
233
234        mClockLayout = (ViewGroup) findViewById(R.id.clock_layout);
235        mClockLayout.setOnClickListener(new View.OnClickListener() {
236                public void onClick(View v) {
237                    final Intent intent = new Intent(AlarmClock.this, ClockPicker.class);
238                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
239                    startActivity(intent);
240                }
241            });
242
243        setClockVisibility(mPrefs.getBoolean(PREF_SHOW_CLOCK, true));
244    }
245
246    @Override
247    protected void onResume() {
248        super.onResume();
249
250        int face = mPrefs.getInt(PREF_CLOCK_FACE, 0);
251        if (mFace != face) {
252            if (face < 0 || face >= AlarmClock.CLOCKS.length)
253                mFace = 0;
254            else
255                mFace = face;
256            inflateClock();
257        }
258    }
259
260    @Override
261    protected void onDestroy() {
262        super.onDestroy();
263        ToastMaster.cancelToast();
264        mCursor.deactivate();
265    }
266
267    protected void inflateClock() {
268        if (mClock != null) {
269            mClockLayout.removeView(mClock);
270        }
271        mClock = mFactory.inflate(CLOCKS[mFace], null);
272        mClockLayout.addView(mClock, 0);
273    }
274
275    @Override
276    public boolean onCreateOptionsMenu(Menu menu) {
277        super.onCreateOptionsMenu(menu);
278
279        mAddAlarmItem = menu.add(0, 0, 0, R.string.add_alarm);
280        mAddAlarmItem.setIcon(android.R.drawable.ic_menu_add);
281
282        mToggleClockItem = menu.add(0, 0, 0, R.string.hide_clock);
283        mToggleClockItem.setIcon(R.drawable.ic_menu_clock_face);
284
285        MenuItem settingsItem = menu.add(0, 0, 0, R.string.settings);
286        settingsItem.setIcon(android.R.drawable.ic_menu_preferences);
287        settingsItem.setIntent(new Intent(this, SettingsActivity.class));
288
289        return true;
290    }
291
292    /**
293     * Only allow user to add a new alarm if there are fewer than
294     * MAX_ALARM_COUNT
295     */
296    @Override
297    public boolean onPrepareOptionsMenu(Menu menu) {
298        super.onPrepareOptionsMenu(menu);
299        mAddAlarmItem.setVisible(mAlarmsList.getChildCount() < MAX_ALARM_COUNT);
300        mToggleClockItem.setTitle(getClockVisibility() ? R.string.hide_clock :
301                                  R.string.show_clock);
302        return true;
303    }
304
305    @Override
306    public boolean onOptionsItemSelected(MenuItem item) {
307        if (item == mAddAlarmItem) {
308            Uri uri = Alarms.addAlarm(getContentResolver());
309            // FIXME: scroll to new item.  mAlarmsList.requestChildRectangleOnScreen() ?
310            String segment = uri.getPathSegments().get(1);
311            int newId = Integer.parseInt(segment);
312            if (Log.LOGV) Log.v("In AlarmClock, new alarm id = " + newId);
313            Intent intent = new Intent(AlarmClock.this, SetAlarm.class);
314            intent.putExtra(Alarms.ID, newId);
315            startActivityForResult(intent, SET_ALARM);
316            return true;
317        } else if (item == mToggleClockItem) {
318            setClockVisibility(!getClockVisibility());
319            saveClockVisibility();
320            return true;
321        }
322
323        return false;
324    }
325
326
327    private boolean getClockVisibility() {
328        return mClockLayout.getVisibility() == View.VISIBLE;
329    }
330
331    private void setClockVisibility(boolean visible) {
332        mClockLayout.setVisibility(visible ? View.VISIBLE : View.GONE);
333    }
334
335    private void saveClockVisibility() {
336        mPrefs.edit().putBoolean(PREF_SHOW_CLOCK, getClockVisibility()).commit();
337    }
338}
339