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