/*
* 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.androsz.electricsleepbeta.alarmclock;
import android.app.AlertDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceScreen;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.Button;
import android.widget.TimePicker;
import android.widget.Toast;
import com.androsz.electricsleepbeta.R;
import com.androsz.electricsleepbeta.preference.HostPreferenceActivity;
/**
* Manages each alarm
*/
public class SetAlarm extends HostPreferenceActivity implements
TimePickerDialog.OnTimeSetListener, Preference.OnPreferenceChangeListener {
// Used to post runnables asynchronously.
// private static final Handler sHandler = new Handler();
/**
* format "Alarm set for 2 days 7 hours and 53 minutes from now"
*/
static String formatToast(final Context context, final long timeInMillis) {
final long delta = timeInMillis - System.currentTimeMillis();
long hours = delta / (1000 * 60 * 60);
final long minutes = delta / (1000 * 60) % 60;
final long days = hours / 24;
hours = hours % 24;
final String daySeq = days == 0 ? "" : days == 1 ? context.getString(R.string.day)
: context.getString(R.string.days, Long.toString(days));
final String minSeq = minutes == 0 ? "" : minutes == 1 ? context.getString(R.string.minute)
: context.getString(R.string.minutes, Long.toString(minutes));
final String hourSeq = hours == 0 ? "" : hours == 1 ? context.getString(R.string.hour)
: context.getString(R.string.hours, Long.toString(hours));
final boolean dispDays = days > 0;
final boolean dispHour = hours > 0;
final boolean dispMinute = minutes > 0;
final int index = (dispDays ? 1 : 0) | (dispHour ? 2 : 0) | (dispMinute ? 4 : 0);
final String[] formats = context.getResources().getStringArray(R.array.alarm_set);
return String.format(formats[index], daySeq, hourSeq, minSeq);
}
/**
* Display a toast that tells the user how long until the alarm goes off.
* This helps prevent "am/pm" mistakes.
*/
static void popAlarmSetToast(final Context context, final int hour, final int minute,
final Alarm.DaysOfWeek daysOfWeek) {
popAlarmSetToast(context, Alarms.calculateAlarm(hour, minute, daysOfWeek).getTimeInMillis());
}
private static void popAlarmSetToast(final Context context, final long timeInMillis) {
final String toastText = formatToast(context, timeInMillis);
final Toast toast = Toast.makeText(context, toastText, Toast.LENGTH_LONG);
ToastMaster.setToast(toast);
toast.show();
}
private AlarmPreference mAlarmPref;
private CheckBoxPreference mEnabledPref;
private int mHour;
private int mId;
private EditTextPreference mLabel;
private int mMinutes;
private Alarm mOriginalAlarm;
private RepeatPreference mRepeatPref;
private boolean mTimePickerCancelled;
private Preference mTimePref;
private CheckBoxPreference mVibratePref;
private void deleteAlarm() {
new AlertDialog.Builder(this).setTitle(getString(R.string.delete_alarm))
.setMessage(getString(R.string.delete_alarm_confirm))
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(final DialogInterface d, final int w) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
Alarms.deleteAlarm(SetAlarm.this, mId);
return null;
}
@Override
protected void onPostExecute(Void result) {
finish();
}
}.execute();
}
}).setNegativeButton(android.R.string.cancel, null).show();
}
@Override
protected int getContentAreaLayoutId() {
return R.xml.alarm_prefs;
}
@Override
public void onBackPressed() {
// In the usual case of viewing an alarm, mTimePickerCancelled is
// initialized to false. When creating a new alarm, this value is
// assumed true until the user changes the time.
if (!mTimePickerCancelled) {
new Thread(new Runnable() {
@Override
public void run() {
saveAlarm();
}
}).start();
}
finish();
}
/**
* Set an alarm. Requires an Alarms.ALARM_ID to be passed in as an extra.
* FIXME: Pass an Alarm object like every other Activity.
*/
@Override
protected void onCreate(final Bundle icicle) {
super.onCreate(icicle);
// Override the default content view.
setContentView(R.layout.set_alarm);
// addPreferencesFromResource(R.xml.alarm_prefs);
// Get each preference so we can retrieve the value later.
mLabel = (EditTextPreference) findPreference("label");
mLabel.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(final Preference p, final Object newValue) {
final String val = (String) newValue;
// Set the summary based on the new label.
p.setSummary(val);
if (val != null && !val.equals(mLabel.getText())) {
// Call through to the generic listener.
return SetAlarm.this.onPreferenceChange(p, newValue);
}
return true;
}
});
mEnabledPref = (CheckBoxPreference) findPreference("enabled");
mEnabledPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(final Preference p, final Object newValue) {
// Pop a toast when enabling alarms.
if (!mEnabledPref.isChecked()) {
popAlarmSetToast(SetAlarm.this, mHour, mMinutes, mRepeatPref.getDaysOfWeek());
}
return SetAlarm.this.onPreferenceChange(p, newValue);
}
});
mTimePref = findPreference("time");
mAlarmPref = (AlarmPreference) findPreference("alarm");
mAlarmPref.setOnPreferenceChangeListener(this);
mVibratePref = (CheckBoxPreference) findPreference("vibrate");
mVibratePref.setOnPreferenceChangeListener(this);
mRepeatPref = (RepeatPreference) findPreference("setRepeat");
mRepeatPref.setOnPreferenceChangeListener(this);
final Intent i = getIntent();
mId = i.getIntExtra(Alarms.ALARM_ID, -1);
if (Log.LOGV) {
Log.v("In SetAlarm, alarm id = " + mId);
}
Alarm alarm = null;
if (mId == -1) {
// No alarm id means create a new alarm.
alarm = new Alarm();
} else {
/* load alarm details from database */
alarm = Alarms.getAlarm(getContentResolver(), mId);
// Bad alarm, bail to avoid a NPE.
if (alarm == null) {
finish();
return;
}
}
mOriginalAlarm = alarm;
updatePrefs(mOriginalAlarm);
// We have to do this to get the save/cancel buttons to highlight on
// their own.
getListView().setItemsCanFocus(true);
getListView().setBackgroundColor(getResources().getColor(R.color.background_dark));
// Attach actions to each button.
Button b = (Button) findViewById(R.id.alarm_save);
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
saveAlarm();
return null;
}
@Override
protected void onPostExecute(Void result) {
finish();
}
}.execute();
}
});
final Button revert = (Button) findViewById(R.id.alarm_revert);
revert.setEnabled(false);
revert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
final int newId = mId;
updatePrefs(mOriginalAlarm);
// "Revert" on a newly created alarm should delete it.
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
if (mOriginalAlarm.id == -1) {
Alarms.deleteAlarm(SetAlarm.this, newId);
} else {
saveAlarm();
}
return null;
}
@Override
protected void onPostExecute(Void result) {
revert.setEnabled(false);
}
}.execute();
}
});
b = (Button) findViewById(R.id.alarm_delete);
if (mId == -1) {
b.setEnabled(false);
} else {
b.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
deleteAlarm();
}
});
}
// The last thing we do is pop the time picker if this is a new alarm.
if (mId == -1) {
// Assume the user hit cancel
mTimePickerCancelled = true;
showTimePicker();
}
}
@Override
public boolean onPreferenceChange(final Preference p, final Object newValue) {
// Asynchronously save the alarm since this method is called _before_
// the value of the preference has changed.
new AsyncTask<Void, Void, Void>() {
@Override
protected void onPreExecute() {
// Editing any preference (except enable) enables the alarm.
if (p != mEnabledPref) {
mEnabledPref.setChecked(true);
}
}
@Override
protected Void doInBackground(Void... params) {
saveAlarm();
return null;
}
@Override
protected void onPostExecute(Void result) {
final Button revert = (Button) findViewById(R.id.alarm_revert);
revert.setEnabled(true);
}
}.execute();
return true;
}
@Override
public boolean onPreferenceTreeClick(final PreferenceScreen preferenceScreen,
final Preference preference) {
if (preference == mTimePref) {
showTimePicker();
}
return super.onPreferenceTreeClick(preferenceScreen, preference);
}
@Override
public void onTimeSet(final TimePicker view, final int hourOfDay, final int minute) {
// onTimeSet is called when the user clicks "Set"
mTimePickerCancelled = false;
mHour = hourOfDay;
mMinutes = minute;
updateTime();
// If the time has been changed, enable the alarm.
mEnabledPref.setChecked(true);
// Save the alarm and pop a toast.
// Enable "Revert" to go back to the original Alarm.
final Button revert = (Button) findViewById(R.id.alarm_revert);
revert.setEnabled(true);
new AsyncTask<Void, Void, Long>() {
@Override
protected Long doInBackground(Void... params) {
return saveAlarm();
}
@Override
protected void onPostExecute(Long result) {
popAlarmSetToast(SetAlarm.this, result);
}
}.execute();
}
private long saveAlarm() {
long time;
final Alarm alarm = new Alarm();
alarm.id = mId;
alarm.enabled = mEnabledPref.isChecked();
alarm.hour = mHour;
alarm.minutes = mMinutes;
alarm.daysOfWeek = mRepeatPref.getDaysOfWeek();
alarm.vibrate = mVibratePref.isChecked();
alarm.label = mLabel.getText();
alarm.alert = mAlarmPref.getAlert();
if (alarm.id == -1) {
time = Alarms.addAlarm(this, alarm);
// addAlarm populates the alarm with the new id. Update mId so that
// changes to other preferences update the new alarm.
mId = alarm.id;
} else {
time = Alarms.setAlarm(this, alarm);
}
return time;
}
private long saveAlarmAndEnableRevert() {
final Button revert = (Button) findViewById(R.id.alarm_revert);
revert.setEnabled(true);
return saveAlarm();
}
private void showTimePicker() {
new TimePickerDialog(this, this, mHour, mMinutes, DateFormat.is24HourFormat(this)).show();
}
private void updatePrefs(final Alarm alarm) {
mId = alarm.id;
mEnabledPref.setChecked(alarm.enabled);
mLabel.setText(alarm.label);
mLabel.setSummary(alarm.label);
mHour = alarm.hour;
mMinutes = alarm.minutes;
mRepeatPref.setDaysOfWeek(alarm.daysOfWeek);
mVibratePref.setChecked(alarm.vibrate);
// Give the alert uri to the preference.
mAlarmPref.setAlert(alarm.alert);
updateTime();
}
private void updateTime() {
if (Log.LOGV) {
Log.v("updateTime " + mId);
}
mTimePref.setSummary(Alarms.formatTime(this, mHour, mMinutes, mRepeatPref.getDaysOfWeek()));
}
@Override
protected int getHeadersResourceId() {
return NO_HEADERS;
}
}