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");
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 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; } });
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.
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.
<activity android:name=".RingBellActivity"></activity>
<activity android:name=".RingBellActivity" android:theme="@android:style/Theme.NoDisplay"></activity>
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)
<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 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>
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(); } }
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); } }
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); }
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); } }
i'd add a checkbox in the main_activity and tie it to preferences, then use that on autostart
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); } }
Did I see the preferences get reset to default?
SharedPreferences prefs = getPreferences(MODE_PRIVATE); timePicker.setCurrentHour(prefs.getInt("hour",10)); timePicker.setCurrentMinute(prefs.getInt("min",0));
SharedPreferences.Editor editor = getPreferences(MODE_PRIVATE).edit(); editor.putInt("hour", timePicker.getCurrentHour()); editor.putInt("min", timePicker.getCurrentMinute()); editor.commit();
manifest.xml…
<activity android:name="CalFlipActivity" android:screenOrientation="portrait">
manifest.xml…
<activity android:name="CalFlipActivity" android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen">
manifest.xml…
<activity android:name="CalFlipActivity" android:theme="@android:style/Theme.Dialog">
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); }