Log to debugger view

Step 1: define a TAG so we use log filters (can also be any string you choose)

	static public String TAG= Activity.class.getName();

Step 2: write [ d=debug, e=error, i=info, v=verbose w=warning

	Log.v(TAG, "Message here");

Button – click monitor onclick and onrelease

		Button bXMinus = (Button) findViewById(R.id.btnXMinus);
		bXMinus.setOnTouchListener(new OnTouchListener() {
		    @Override
		    public boolean onTouch(View v, MotionEvent event) {
		        if(event.getAction() == MotionEvent.ACTION_DOWN) {
		        	sendMessage("jog x -1");
		        } else if (event.getAction() == MotionEvent.ACTION_UP) {
		        	sendMessage("jog x 0");
		        }
		        return true;
		    }
		});

Android and Webview

As I found out when I was working with the canvas, graphic are clunky – well about the same as everything else. The stuff I want to do is not so pretty, it is more functional. I want nice looking dial or pretty graph of live data. Why not use the web and access the android data as a service. Good idea. This post outlines the experiments.

Basics – a simple web pages as an app.
1. Create a project with a main activity and webview as about all you see.
2. Update the manifest to include internet access? – even for local?
3. create a folder called html in the assets folder of the project
4. Fill it with a file called index.html
5. Put some stupid stuff in index.html
6. [special bonus for including css file]
7. customize mainactivity to allow javascript and open the local url

<uses-permission android:name="android.permission.INTERNET" />

Experiment 1 – My own

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		WebView myWebView = (WebView) findViewById(R.id.webView1);
		String summary = "<html><body>You scored <b>192</b> points.</body></html>";
		 myWebView.loadData(summary, "text/html", null);
		 		//myWebView.loadUrl("http://www.example.com");	
	}

}

Experiment 2 – With the world

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
                WebView myWebView = (WebView) findViewById(R.id.webView1);
                myWebView.loadUrl("http://www.google.com");
	}

}

Experiment 3 – my own file (with puppy graphic )
{it didn’t work because I used “android_assets/html/index.html” instead of final “android_asset/html/index.html”)
put a sample index.html file in assets/html/index.html
add a sample graphic at assets/html/puppy.jpg

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
        WebView myWebView = (WebView) findViewById(R.id.webView1);
        myWebView.loadUrl("file:///android_asset/html/index.html");

	}

}
<!doctype html>
<html lang=es>
<head>
<title>Title</title>
</head>
<body>
<h1>Hey there</h1>
<div>
  <img src="file:///android_asset/html/puppy.jpg">
</div>
<div>
The time is 
<span id="time">Time</span>
</div>
</body>
</html>

So then I got bold – CSS and jQuery!
1. Add the css file
2. add the jquery.min.js file
3. Change mainactivity to allow javascript run

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		WebView myWebView = (WebView) findViewById(R.id.webView1);
        WebSettings websettings=myWebView.getSettings();
        websettings.setJavaScriptEnabled(true);
        myWebView.loadUrl("file:///android_asset/html/index.html");

	}

}
html {
   background-color: #aa2020;
}
h1 { 
   color: #0000ff;
}
img { 
 width: 90%;
 margin: auto;
}
<!doctype html>
<html lang=en>
<head>
<title>Title</title>
<link rel="stylesheet" type="text/css" href="main.css" />
<script src="jquery-2.0.3.min.js"></script>
<script type="text/javascript">
var counter=0;
$( document ).ready(function() { 
    setInterval(tick,1000);
});
function tick(){
	counter++;
    $( "#time" ).text(counter);
}
</script>
</head>
<body>
<h1>Hey there</h1>
<div>
Elapsed Seconds: 
<span id="time">???</span>
</div>
<div>
  <img src="file:///android_asset/html/puppy.jpg">
</div>
</body>
</html>

Extra special awesomeness part 1 – Dynamic graph demo
create the graph.html file (cut and paste from the example)
include Flot and jquery and rember to
include flot’s example.css too so it will show up
Remove the relative links from the example html

Now the fun begins – combine the two
1. Create an interface in the main activity and attache it using the addJavascriptInterface call
2. Add references to that object in the javascript
note: save time by changing manifest versions to max at 16 not 17(where it needs a special annotation syntax)

public class MainActivity extends Activity {
	Random random=new Random();
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		WebView myWebView = (WebView) findViewById(R.id.webView1);
        WebSettings websettings=myWebView.getSettings();
        websettings.setJavaScriptEnabled(true);
        myWebView.addJavascriptInterface(new JavaScriptInterface(this), "MyAndroid");
        myWebView.loadUrl("file:///android_asset/html/index.html");

	}
    public class JavaScriptInterface {
        Context mContext;
 
        /** Instantiate the interface and set the context */
        JavaScriptInterface(Context c) {
            mContext = c;
        }
 
        /** Show a toast from the web page */
        public String showToast(String toast) {
            Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
            return toast;
        }
 
        public int randomInt(){
            return random.nextInt(100);
        }
    }

}
<!doctype html>
<html lang=en>
<head>
<title>Title</title>
<link rel="stylesheet" type="text/css" href="main.css" />
<script src="jquery-2.0.3.min.js"></script>
<script type="text/javascript">
var counter=0;
$( document ).ready(function() { 
    setInterval(tick,1000);
});
function tick(){
var result=MyAndroid.randomInt();
	counter++;
    $( "#time" ).text(counter);
    $("#time2").text(" uh "+result);
}
</script>
</head>
<body>
<h1>Hey there</h1>
<div>
Elapsed Seconds: 
<span id="time">???</span>
<span id="time2">???</span>
</div>
<div>
<a href="testgraph.html">Test Grapht</a>
</div>
<div>
  <img src="file:///android_asset/html/puppy.jpg">
</div>
</body>
</html>

NOW TIE IT ALL TOGHETHER


public class MainActivity extends Activity {
	Random random=new Random();
	int lastRandom=50;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		WebView myWebView = (WebView) findViewById(R.id.webView1);
        WebSettings websettings=myWebView.getSettings();
        websettings.setJavaScriptEnabled(true);
        myWebView.addJavascriptInterface(new JavaScriptInterface(this), "MyAndroid");
        myWebView.loadUrl("file:///android_asset/html/index.html");

	}
    public class JavaScriptInterface {
        Context mContext;
 
        /** Instantiate the interface and set the context */
        JavaScriptInterface(Context c) {
            mContext = c;
        }
 
        /** Show a toast from the web page */
        public String showToast(String toast) {
            Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
            return toast;
        }
 
        public int randomInt(){
        	lastRandom+=random.nextInt(5)-2;
            return lastRandom;
        }
    }

}

<!doctype html>
<html lang=en>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Flot Examples: Real-time updates</title>
	<link href="examples.css" rel="stylesheet" type="text/css">
	<script language="javascript" type="text/javascript" src="jquery-2.0.3.min.js"></script>
	<script language="javascript" type="text/javascript" src="jquery.flot.min.js"></script>
	<script type="text/javascript">
	$(function() {

		// We use an inline data source in the example, usually data would
		// be fetched from a server

		var data = [],
			totalPoints = 300;

		function getRandomData() {
			if (data.length > 0)
				data = data.slice(1);

			// Do a random walk

			while (data.length < totalPoints) {
				data.push(MyAndroid.randomInt());
			}

			// Zip the generated y values with the x values
			var res = [];
			for (var i = 0; i < data.length; ++i) {
				res.push([i, data[i]])
			}

			return res;
		}

		// Set up the control widget

		var updateInterval = 500;
		$("#updateInterval").val(updateInterval).change(function () {
			var v = $(this).val();
			if (v && !isNaN(+v)) {
				updateInterval = +v;
				if (updateInterval < 1) {
					updateInterval = 1;
				} else if (updateInterval > 2000) {
					updateInterval = 2000;
				}
				$(this).val("" + updateInterval);
			}
		});

		var plot = $.plot("#placeholder", [ getRandomData() ], {
			series: {
				shadowSize: 0	// Drawing is faster without shadows
			},
			yaxis: {
				min: 0,
				max: 100
			},
			xaxis: {
				show: false
			}
		});

		function update() {

			plot.setData([getRandomData()]);

			// Since the axes don't change, we don't need to call plot.setupGrid()

			plot.draw();
			setTimeout(update, updateInterval);
		}

		update();

		// Add the Flot version string to the footer

		$("#footer").prepend("Flot " + $.plot.version + " – ");
	});

	</script>
</head>
<body>

	<div id="header">
		<h2>Real-time updates</h2>
	</div>

	<div id="content">

		<div class="demo-container">
			<div id="placeholder" class="demo-placeholder"></div>
		</div>

		<p>You can update a chart periodically to get a real-time effect by using a timer to insert the new data in the plot and redraw it.</p>

		<p>Time between updates: <input id="updateInterval" type="text" value="" style="text-align: right; width:5em"> milliseconds</p>

	</div>

	<div id="footer">
		Copyright © 2007 - 2013 IOLA and Ole Laursen
	</div>

</body>
</html>

HEE HEE. Not a bad afternoons expermimentation. Now to combine it with the Bluetooth Experiments of early October and a nifty bit of hardware called HH ODB Advanced – to use CAN Open to read realtime information.
Stay Tuned.

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();

QUICKIES

Screen starts/stays in PORTRAIT mode:

manifest.xml…

<activity android:name="CalFlipActivity" android:screenOrientation="portrait">

No title bar on screen

manifest.xml…

<activity android:name="CalFlipActivity" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">

Activity Looks Like Dialog

manifest.xml…

<activity android:name="CalFlipActivity" android:theme="@android:style/Theme.Dialog">

 

Menus – Option Menu

Android has a couple menu types.  The “option” menu is one that is access by using the unit’s menu button.  Since it is built into the view, you only need to

The setup – some constants:

	final public int MENU_GROUP=0;
	final public int MENU_ADD=0;
	final public int MENU_EDIT=1;
	final public int MENU_DELETE=2;

The setup – build the list

@Override 
public boolean onCreateOptionsMenu(Menu menu) {
 	menu.add(MENU_GROUP, MENU_ADD, 0, "add");
    	menu.add(MENU_GROUP, MENU_EDIT, 0, "edit");
    	menu.add(MENU_GROUP, MENU_DELETE, 0, "delete");
    	return super.onCreateOptionsMenu(menu); 
}

The handler – respond to the options

@Override 
public boolean onOptionsItemSelected(MenuItem item) {	
    	switch(item.getItemId()) { 
    	case MENU_ADD:
    		Intent intent = new Intent(getBaseContext(), editActivity.class);
    		intent.putExtra("ID", 0);
    		startActivity(intent);
    		return true;
    	case MENU_EDIT: 
    		return true;
    	case MENU_DELETE: 
    		return true;
    	} 
    	return super.onOptionsItemSelected(item);
}