An Activity Without UI

I suppose the proper way would be to change the activity into a Service then use the startService() method, but this came up in when I Googled. Oh’so much less coding and I was back to work in seconds.

Change this:
<activity  android:name=".RingBellActivity"></activity>
To
<activity  android:name=".RingBellActivity"  android:theme="@android:style/Theme.NoDisplay"></activity>

Schedule a Job for the Time You Choose

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?

Quick and dirty Custom Preferences

in the activity onCreate…
SharedPreferences prefs = getPreferences(MODE_PRIVATE);
timePicker.setCurrentHour(prefs.getInt("hour",10));
timePicker.setCurrentMinute(prefs.getInt("min",0));
in the activity onDestroy…
SharedPreferences.Editor editor = getPreferences(MODE_PRIVATE).edit();
 editor.putInt("hour", timePicker.getCurrentHour());
 editor.putInt("min", timePicker.getCurrentMinute());    	 
 editor.commit();