There are a bunch of pieces on this one.
You need:
1. an activity(RingBellActivity) to do something
2. a broadcast receiver (AlarmReceiver) that ties to the alarm manager to receive the alarm request
3. a setup activity(MainActivity) to schedule the task(tied to the MAIN, LAUNCHER)
4. an activity to schedule it next time the android reboots
Special bonus for…
5. A way to turn it off
6. a behind the scenes activity with no user interaction(RingBellService)
Step 0 – xml files
res/layout/main_activity.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TimePicker android:id="@+id/timePicker1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_marginTop="17dp" /> <Button android:id="@+id/btnGo" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/timePicker1" android:layout_marginTop="44dp" android:text="Wakeme" /> <Button android:id="@+id/btnStop" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/btnGo" android:text="Cancel" /> <Button android:id="@+id/btnTest" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@+id/btnStop" android:text="Test" /> </RelativeLayout>
manifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.steelmarble.ontime" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="15" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".RingBellActivity" android:theme="@android:style/Theme.NoDisplay"> </activity> <receiver android:name=".AlarmReceiver" android:process=":remote"></receiver> <service android:name="RingBellService"></service> <receiver android:name="Autostart"> <intent-filter> <action android:name="android.intent.action.QUICKBOOT_POWERON" /> <action android:name="android.intent.action.BOOT_COMPLETED" /> <action android:name="com.steelmarble.ontime.AUTOSTART" /> </intent-filter> </receiver> </application> </manifest>
Step 1
public class RingBellActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); NotificationManager nm = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new Notification(R.drawable.ic_launcher, "DingALing", System.currentTimeMillis()); // This is intent, we want to launch when user clicks on the notification. Intent intentTL = new Intent(this, MainActivity.class); notification.setLatestEventInfo(this, "TIME", "To Do Something!", PendingIntent.getActivity(this, 0, intentTL, PendingIntent.FLAG_CANCEL_CURRENT)); notification.flags = Notification.DEFAULT_LIGHTS | Notification.FLAG_AUTO_CANCEL; nm.notify(1, notification); finish(); } }
2. a broadcast receiver (AlarmReceiver) that ties to the alarm manager to receive the alarm request
public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent scheduledIntent = new Intent(context, RingBellActivity.class); scheduledIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(scheduledIntent); } }
3. a setup activity(MainActivity) to schedule the task(tied to the MAIN, LAUNCHER)
public class MainActivity extends Activity { public static final String PREFERENCE_FILENAME = "AppGamePrefs"; TimePicker timePicker; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // setup UI time scheuler timePicker=(TimePicker) findViewById(R.id.timePicker1); SharedPreferences prefs = getSharedPreferences(PREFERENCE_FILENAME,MODE_PRIVATE); timePicker.setCurrentHour(prefs.getInt("hour",10)); timePicker.setCurrentMinute(prefs.getInt("min",0)); // Schedule it... Button b = (Button) findViewById(R.id.btnGo); b.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(System.currentTimeMillis()); Date stamp = cal.getTime(); stamp.setHours(timePicker.getCurrentHour()); stamp.setMinutes(timePicker.getCurrentMinute()); PendingIntent pendingIntent = getAlarmPendingIntent(MainActivity.this); AlarmManager manager=(AlarmManager)getSystemService(Context.ALARM_SERVICE); manager.cancel(pendingIntent); // kill any other outstanding requests first manager.setRepeating(AlarmManager.RTC_WAKEUP, stamp.getTime(), 10*1000, pendingIntent); // or as a one-shot //manager.set(AlarmManager.RTC_WAKEUP, stamp.getTime(), pendingIntent); finish(); } }); // unschedule Button b2 = (Button) findViewById(R.id.btnStop); b2.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { AlarmManager manager=(AlarmManager)getSystemService(Context.ALARM_SERVICE); manager.cancel(getAlarmPendingIntent(MainActivity.this)); finish(); } }); // special bonus bonus - test the reboot code without rebooting // click wakeme(to set), cancel(to turn it off), test(fake the reboot by calling custom intent) Button b3 = (Button) findViewById(R.id.btnTest); b3.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { Intent intent = new Intent(); intent.setAction(Autostart.CUSTOM_INTENT); sendBroadcast(intent); } }); } @Override public void onDestroy(){ // save the preferences super.onDestroy(); SharedPreferences.Editor editor = getSharedPreferences(PREFERENCE_FILENAME,MODE_PRIVATE).edit(); editor.putInt("hour", timePicker.getCurrentHour()); editor.putInt("min", timePicker.getCurrentMinute()); editor.commit(); } // one stop shop so we can set/cancel without recoding all over the place public static PendingIntent getAlarmPendingIntent(Context context){ Intent intent= new Intent(context,AlarmReceiver.class); return PendingIntent.getBroadcast(context,0, intent,0); }
4. an activity to schedule it next time the android reboots
public class Autostart extends BroadcastReceiver { public static final String CUSTOM_INTENT = "com.steelmarble.ontime.AUTOSTART"; @Override public void onReceive(Context context, Intent arg1) { String text="Coming right up"; Calendar cal = Calendar.getInstance(); cal.setTimeInMillis(System.currentTimeMillis()); SharedPreferences prefs = context.getSharedPreferences(MainActivity.PREFERENCE_FILENAME,context.MODE_PRIVATE); Date dStamp= cal.getTime(); Date dNow = cal.getTime(); dStamp.setHours(prefs.getInt("hour",1)); dStamp.setMinutes(prefs.getInt("minute",23)); Long stamp = dStamp.getTime(); Long now = dNow.getTime(); if(now>stamp){ // to late for today, avoid immediate trigger and schedule for tomorrow stamp+= AlarmManager.INTERVAL_DAY; // add one day text="Scheduled for tomorrow"; } NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = new Notification(R.drawable.ic_launcher, "DingALing", System.currentTimeMillis()); // This is intent, we want to launch when user clicks on the notification. Intent intentTL = new Intent(context, MainActivity.class); notification.setLatestEventInfo(context, "TIME", text, PendingIntent.getActivity(context, 0, intentTL, PendingIntent.FLAG_CANCEL_CURRENT)); notification.flags = Notification.DEFAULT_LIGHTS | Notification.FLAG_AUTO_CANCEL; nm.notify(1, notification); PendingIntent pendingIntent=MainActivity.getAlarmPendingIntent(context); AlarmManager manager=(AlarmManager)context.getSystemService(Context.ALARM_SERVICE); manager.cancel(pendingIntent); manager.setRepeating(AlarmManager.RTC_WAKEUP, stamp, 10*1000, pendingIntent); } }
Special bonus for…
5. A way to turn it off
i'd add a checkbox in the main_activity and tie it to preferences, then use that on autostart
6. a behind the scenes activity with no user interaction (sounds like a service to me or a fast alternative)
6a. Create a service
public class RingBellService extends Service { @Override public IBinder onBind(Intent arg0) { // TODO Auto-generated method stub return null; } @Override public void onStart(Intent intent, int startid) { Toast.makeText(this, "RingBellService", Toast.LENGTH_LONG).show(); } }
6b. Change the AlarmReceiver to issue a
public class AlarmReceiver extends BroadcastReceiver { /* Start some run once service */ @Override public void onReceive(Context context, Intent intent) { Intent scheduledIntent = new Intent(context, RingBellService.class); scheduledIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startService(scheduledIntent); } }
Bugs and More
Did I see the preferences get reset to default?