Thursday, May 31, 2012

Vibrate with a given pattern

Last exercise demonstrate basic of android.os.Vibrator. The method vibrate (long[] pattern, int repeat) of Vibrator cause vibrate with a given pattern.

Vibrate with a given pattern


package com.exercise.AndroidVibrator;

import android.app.Activity;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;

public class AndroidVibratorActivity extends Activity {
 
 Button buttonVibrate1, buttonVibrate2, buttonVibrate3;
 
 Vibrator myVibrator;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        buttonVibrate1 = (Button)findViewById(R.id.buttonVibrate1);
        buttonVibrate2 = (Button)findViewById(R.id.buttonVibrate2);
        buttonVibrate3 = (Button)findViewById(R.id.buttonVibrate3);
        
        myVibrator = (Vibrator)getSystemService(AndroidVibratorActivity.VIBRATOR_SERVICE);
        
        buttonVibrate1.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    myVibrator.vibrate(500);
   }});
        
        buttonVibrate2.setOnTouchListener(new OnTouchListener(){

   @Override
   public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();
    
    if(action == MotionEvent.ACTION_DOWN){
     myVibrator.vibrate(1000);
    }else if(action == MotionEvent.ACTION_UP){
     myVibrator.cancel();
    }
    
    return true;
   }});
        
        buttonVibrate3.setOnTouchListener(new OnTouchListener(){

   @Override
   public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();
    
    if(action == MotionEvent.ACTION_DOWN){
     
     long[] pattern = {
      50,   //Off before vibration
      100, 100,  //on-off
      100, 1000, //on-off
     };
     
     myVibrator.vibrate(pattern, 0); //repeat from pattern[0]
    }else if(action == MotionEvent.ACTION_UP){
     myVibrator.cancel();
    }
    
    return true;
   }});
        
    }
}


main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <Button
        android:id="@+id/buttonVibrate1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Vibrate for 500ms" />
    <Button
        android:id="@+id/buttonVibrate2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Touch to Vibrate (max. 1 sec.)" />
    <Button
        android:id="@+id/buttonVibrate3"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Touch to Vibrate Pattern" />

</LinearLayout>


"android.permission.VIBRATE" is needed.


Program Android Vibrator

android.os.Vibrator operates the vibrator on the device.

Program Android Vibrator


Your code call getSystemService(AndroidVibratorActivity.VIBRATOR_SERVICE) to retrieve a Vibrator for interacting with the vibration hardware. In the example, touch on button1 to generate vibration for 500ms. Touch and hold on button2 to generate vibration for max 1 second, or release to stop.

You have to modify AndroidManifest.xml to add permission of "android.permission.VIBRATE".

package com.exercise.AndroidVibrator;

import android.app.Activity;
import android.os.Bundle;
import android.os.Vibrator;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;

public class AndroidVibratorActivity extends Activity {
 
 Button buttonVibrate1, buttonVibrate2;
 
 Vibrator myVibrator;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        buttonVibrate1 = (Button)findViewById(R.id.buttonVibrate1);
        buttonVibrate2 = (Button)findViewById(R.id.buttonVibrate2);
        
        myVibrator = (Vibrator)getSystemService(AndroidVibratorActivity.VIBRATOR_SERVICE);
        
        buttonVibrate1.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    myVibrator.vibrate(500);
   }});
        
        buttonVibrate2.setOnTouchListener(new OnTouchListener(){

   @Override
   public boolean onTouch(View v, MotionEvent event) {
    int action = event.getAction();
    
    if(action == MotionEvent.ACTION_DOWN){
     myVibrator.vibrate(1000);
    }else if(action == MotionEvent.ACTION_UP){
     myVibrator.cancel();
    }
    
    return true;
   }});
        
    }
}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />

    <Button
        android:id="@+id/buttonVibrate1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Vibrate for 500ms" />
    <Button
        android:id="@+id/buttonVibrate2"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Touch to Vibrate (max. 1 sec.)" />

</LinearLayout>


Next:
- Vibrate with a given pattern


Wednesday, May 30, 2012

Generate Notification using Notification.Builder for Android 3.0

Android 3.0 (API Level 11) introduce Notification.Builder, allows easier control over all the flags, as well as help constructing the typical notification layouts.

It's a example of using Notification.Builder, modified from the previous exercise "schedule a repeating alarm".

Generate Notification using Notification.Builder for Android 3.0


Change the code of AlarmReceiver.java in "schedule a repeating alarm", to generate Notificaton.

package com.exercise.AndroidTime;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class AlarmReceiver extends BroadcastReceiver {

 @Override
 public void onReceive(Context context, Intent arg1) {
  Toast.makeText(context, "Alarm received!", Toast.LENGTH_LONG).show();
  buildNotification(context);
  
 }
 
 private void buildNotification(Context context){
  NotificationManager notificationManager 
  = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
  Notification.Builder builder = new Notification.Builder(context);
  
  Intent intent = new Intent(context, AndroidTimeActivity.class);
  PendingIntent pendingIntent 
  = PendingIntent.getActivity(context, 0, intent, 0);
  
  builder
  .setSmallIcon(R.drawable.ic_launcher)
  .setContentTitle("ContentTitle")
  .setContentText("ContentText")
  .setContentInfo("ContentInfo")
  .setTicker("Ticker")
  .setLights(0xFFFF0000, 500, 500) //setLights (int argb, int onMs, int offMs)
  .setContentIntent(pendingIntent)
  .setAutoCancel(true);
  
  Notification notification = builder.getNotification();
  
  notificationManager.notify(R.drawable.ic_launcher, notification);
 }

}


Please notice that you have to change your project to target API Level 11, in order to use Notification.Builder.

Download the files.


3D rotate for RenderScript - rsMatrixRotate()

Refer to the RenderScript.rs in the example "Perform transform of Translate and Rotate on RenderScript", the rsMatrixRotate() function define the rotation.
void rsMatrixRotate(rs_matrix4x4 *m, float rot, float x, float y, float z)

rsMatrixRotate() modified


Modify RenderScript.rs of last exercise "Draw circle in Android RenderScript".
//declare RendorScript Version
#pragma version(1)

//declare package
#pragma rs java_package_name(com.exercise.AndroidRenderScript);

//include graphics library
#include "rs_graphics.rsh"

rs_mesh my_rs_mesh;

float rotation;

void init(){
 rotation = 0.0f;
}

int root(){
 //set background color
 rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
 //Transform something
 rs_matrix4x4 matrix;
 rsMatrixLoadIdentity(&matrix);
 rsMatrixTranslate(&matrix, 100.0f, 100.0f, 0.0f);
 rsMatrixRotate(&matrix, rotation++, 1.0f, 1.0f, 1.0f);
 rsgProgramVertexLoadModelMatrix(&matrix);
 
 //draw something
 rsgDrawMesh(my_rs_mesh);
 
 //repeat 20ms
 return 20;
}


Tuesday, May 29, 2012

Draw circle in Android RenderScript

Modify MyRSSurfaceView.java from last exercise "Perform transform of Translate and Rotate on RenderScript", to draw circle by combine a series of Triangle. Refer to the code, the larger the constant number_of_section, the more the shape close to circle.

circle in Android RenderScript


package com.exercise.AndroidRenderScript;

import android.content.Context;
import android.renderscript.Mesh;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.renderscript.RenderScriptGL.SurfaceConfig;

public class MyRSSurfaceView extends RSSurfaceView {
 
 private RenderScriptGL renderScriptGL;
 private ScriptC_RenderScript myScriptC;

 public MyRSSurfaceView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
  final RenderScriptGL.SurfaceConfig surfaceConfig
   = new SurfaceConfig();
  renderScriptGL = createRenderScriptGL(surfaceConfig);
  
  myScriptC = new ScriptC_RenderScript(
    renderScriptGL, getResources(), R.raw.renderscript);

  myScriptC.set_my_rs_mesh(createMesh());
  
  renderScriptGL.bindRootScript(myScriptC);
 }
 
 private Mesh createMesh(){
  
  double radius = 100.0;
  double angleInDegree, angleInRadian;
  
  final int number_of_section = 18;
  double ptX, ptY;
  
  Mesh.TriangleMeshBuilder myMesh 
  = new Mesh.TriangleMeshBuilder(
    renderScriptGL, 
    2, 
    Mesh.TriangleMeshBuilder.COLOR);

  //index 0
  myMesh.addVertex(0, 0);
  
  //index 1
  angleInDegree = 0;
  angleInRadian = Math.toRadians(angleInDegree);
  ptX = radius * Math.cos(angleInRadian);
  ptY = radius * Math.sin(angleInRadian);
  myMesh.addVertex((float)ptX, (float)ptY);
  
  for(int i=0; i<number_of_section; i++){
   angleInDegree += 360/number_of_section;
   angleInRadian = Math.toRadians(angleInDegree);
   ptX = radius * Math.cos(angleInRadian);
   ptY = radius * Math.sin(angleInRadian);
   myMesh.addVertex((float)ptX, (float)ptY);
   
   myMesh.addTriangle(0, i+2, i+1);
   
  }

  return(myMesh.create(true));
 };

}


Download the files.

Draw square in Android RenderScript

Modify MyRSSurfaceView.java from last exercise "Perform transform of Translate and Rotate on RenderScript", to draw square by combine two Triangle.

package com.exercise.AndroidRenderScript;

import android.content.Context;
import android.renderscript.Mesh;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.renderscript.RenderScriptGL.SurfaceConfig;

public class MyRSSurfaceView extends RSSurfaceView {
 
 private RenderScriptGL renderScriptGL;
 private ScriptC_RenderScript myScriptC;

 public MyRSSurfaceView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
  final RenderScriptGL.SurfaceConfig surfaceConfig
   = new SurfaceConfig();
  renderScriptGL = createRenderScriptGL(surfaceConfig);
  
  myScriptC = new ScriptC_RenderScript(
    renderScriptGL, getResources(), R.raw.renderscript);

  myScriptC.set_my_rs_mesh(createMesh());
  
  renderScriptGL.bindRootScript(myScriptC);
 }
 
 private Mesh createMesh(){
  Mesh.TriangleMeshBuilder myMesh 
  = new Mesh.TriangleMeshBuilder(
    renderScriptGL, 
    2, 
    Mesh.TriangleMeshBuilder.COLOR);
  /*  0   2
   *   +---+
   *   |\  |
   *   | \ |
   *   |  \|
   *   +---+
   *   1   3
   */  
  
  
  myMesh.addVertex(0, 0);  //- 0
  myMesh.addVertex(0, 100); //- 1
  myMesh.addVertex(100, 0); //- 2
  myMesh.addVertex(100, 100); //- 3
  
  myMesh.addTriangle(0, 1, 3);
  myMesh.addTriangle(0, 3, 2);

  return(myMesh.create(true));
 };

}


Draw square in Android RenderScript


Download the files.

Monday, May 28, 2012

Perform transform of Translate and Rotate on RenderScript

Perform transform of Translate and Rotate on RenderScript


Modify RenderScript.rs of last exercise "Program Renderscript on Android" to perform transform of Translate and Rotate - to rotate the rendered triangle.

RenderScript.rs
//declare RendorScript Version
#pragma version(1)

//declare package
#pragma rs java_package_name(com.exercise.AndroidRenderScript);

//include graphics library
#include "rs_graphics.rsh"

rs_mesh my_rs_mesh;

float rotation;

void init(){
 rotation = 0.0f;
}

int root(){
 //set background color
 rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
 //Transform something
 rs_matrix4x4 matrix;
 rsMatrixLoadIdentity(&matrix);
 rsMatrixTranslate(&matrix, 100.0f, 100.0f, 0.0f);
 rsMatrixRotate(&matrix, rotation++, 0.0f, 0.0f, 1.0f);
 rsgProgramVertexLoadModelMatrix(&matrix);
 
 //draw something
 rsgDrawMesh(my_rs_mesh);
 
 //repeat 20ms
 return 20;
}


Download the files.

Next:
- Draw square in Android RenderScript

Program Renderscript on Android

Renderscript (introduced in Android 3.0) offers a high performance 3D graphics rendering and compute API at the native level that you write in C (C99 standard).

The Renderscript runtime operates at the native level and still needs to communicate with the Android VM, so the way a Renderscript application is setup is different from a pure VM application. An application that uses Renderscript is still a traditional Android application that runs in the VM, but you write Renderscript code for the parts of your program that require it. Using Renderscript can be as simple as offloading a few math calculations or as complicated as rendering an entire 3D game. No matter what you use it for, Renderscript remains platform independent, so you do not have to target multiple architectures (for example, ARM v5, ARM v7, x86).

The Renderscript system adopts a control and slave architecture where the low-level Renderscript runtime code is controlled by the higher level Android system that runs in a virtual machine (VM). The Android VM still retains all control of memory management and binds memory that it allocates to the Renderscript runtime, so the Renderscript code can access it. The Android framework makes asynchronous calls to Renderscript, and the calls are placed in a message queue and processed as soon as possible.


It's a very basic example of Program Renderscript on Android. It simple draw a triangle on RSSurfaceView.

Renderscript on Android


First of all, to use Renderscript, we have to define Renderscript code in .rs(Renderscript) and .rsh(Renderscript header) files in the src/ directory of Android project.

RenderScript.rs
The init() method will be called when it's first time loaded. The root() will be called every time the script run. The return parameter of root() define the repeat interval of the script.
//declare RendorScript Version
#pragma version(1)

//declare package
#pragma rs java_package_name(com.exercise.AndroidRenderScript);

//include graphics library
#include "rs_graphics.rsh"

rs_mesh my_rs_mesh;

void init(){

}

int root(){
 //set background color
 rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f);
 
 //draw something
 rsgDrawMesh(my_rs_mesh);
 
 //repeat 20ms
 return 20;
}

Once you save the rs file, the coresponding files ScriptC_RenderScript.java and /res/raw/renderscript.bc will be generated. The file name depends on your rs file



Create MyRSSurfaceView class extends RSSurfaceView.
The class ScriptC_RenderScript and R.raw.renderscript are generated from the rs file.
package com.exercise.AndroidRenderScript;

import android.content.Context;
import android.renderscript.Mesh;
import android.renderscript.RSSurfaceView;
import android.renderscript.RenderScriptGL;
import android.renderscript.RenderScriptGL.SurfaceConfig;

public class MyRSSurfaceView extends RSSurfaceView {
 
 private RenderScriptGL renderScriptGL;
 private ScriptC_RenderScript myScriptC;

 public MyRSSurfaceView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
  final RenderScriptGL.SurfaceConfig surfaceConfig
   = new SurfaceConfig();
  renderScriptGL = createRenderScriptGL(surfaceConfig);
  
  myScriptC = new ScriptC_RenderScript(
    renderScriptGL, getResources(), R.raw.renderscript);

  //Create something...
  Mesh.TriangleMeshBuilder myMesh 
   = new Mesh.TriangleMeshBuilder(
     renderScriptGL, 
     2, 
     Mesh.TriangleMeshBuilder.COLOR);
  myMesh.addVertex(0, 0);
  myMesh.addVertex(0, 100);
  myMesh.addVertex(100, 0);
  myMesh.addTriangle(0, 1, 2);
  Mesh mesh = myMesh.create(true);
  myScriptC.set_my_rs_mesh(mesh);
  //
  
  renderScriptGL.bindRootScript(myScriptC);
 }

}

Finally, modify the main activity to set MyRSSurfaceView as content view.

package com.exercise.AndroidRenderScript;

import android.app.Activity;
import android.os.Bundle;

public class AndroidRenderScriptActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new MyRSSurfaceView(this));
    }
}


Download the files.


Next:
- Perform transform of Translate and Rotate on RenderScript


Saturday, May 26, 2012

Debug Android app on BlueStacks App Player for Windows (beta-1)


With BlueStacks App Player for Windows (beta-1), we can deploy Android app from Eclipse to BlueStacks directly. It's much faster than the Emulator of Android SDK. ~ but nobody will and nobody can confirm the compatibility.

In my own trial, I haven't set anything, it install and run on BlueStacks automatically, no port setting, no extra APK (Launcher Pro or ADW) installed. (Here is a thread explain How to use BlueStack as Android development emulator. Actually I haven't do anything. I list here for your reference in case any problem.)

My platform:
- Host OS: Windows 8 Consumer Preview
- Eclipse Indigo Service Release 2
- ADT 18
- Android SDK Tools 19
- BlueStacks App Player for Windows (beta-1)

It's my exercise of OpenStreetMap run on BlueStacks App Player for Windows (beta-1).

OpenStreetMap run on BlueStacks

You can see a emulator-5554 is listed in my devices list.



I also describe here how to enable the devices view in Eclipse:
- Click Window -> Show View -> Other...



- Expand Android and select Devices.



BlueStacks beta-1 available to download now, run Android on Windows

BlueStacks Beta Demo

A walk-through of BlueStacks' Android App Player for PC, beta version. Download now at http://BlueStacks.com

Next:
- Debug Android app on BlueStacks App Player for Windows (beta-1)


Friday, May 25, 2012

schedule a repeating alarm

To schedule a repeating alarm, call setRepeating(int type, long triggerAtTime, long interval, PendingIntent operation) method of AlarmManager instead of set(int type, long triggerAtTime, PendingIntent operation).

Schedule a repeating alarm

Modify from last post "Cancel alarm with a matching PendingIntent". (Also change using TimePicker widget instead of TimePickerDialog)

package com.exercise.AndroidTime;

import java.util.Calendar;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.TimePicker;

public class AndroidTimeActivity extends Activity {
 
 TimePicker myTimePicker;
 Button buttonstartSetDialog;
 Button buttonCancelAlarm;
 TextView textAlarmPrompt;
 
 TimePicker timePicker;
 CheckBox optRepeat;
 
 final static int RQS_1 = 1;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        timePicker = (TimePicker)findViewById(R.id.picker);
        optRepeat = (CheckBox)findViewById(R.id.optrepeat);
        textAlarmPrompt = (TextView)findViewById(R.id.alarmprompt);
        
        buttonstartSetDialog = (Button)findViewById(R.id.startSetDialog);
        buttonstartSetDialog.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    Calendar calNow = Calendar.getInstance();
    Calendar calSet = (Calendar) calNow.clone();

    calSet.set(Calendar.HOUR_OF_DAY, timePicker.getCurrentHour());
    calSet.set(Calendar.MINUTE, timePicker.getCurrentMinute());
    calSet.set(Calendar.SECOND, 0);
    calSet.set(Calendar.MILLISECOND, 0);
    
    if(calSet.compareTo(calNow) <= 0){
     //Today Set time passed, count to tomorrow
     calSet.add(Calendar.DATE, 1);
    }
    
    setAlarm(calSet, optRepeat.isChecked());
    
   }});

        buttonCancelAlarm = (Button)findViewById(R.id.cancel);
        buttonCancelAlarm.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    cancelAlarm();
   }});
        
    }

 private void setAlarm(Calendar targetCal, boolean repeat){
  
  Intent intent = new Intent(getBaseContext(), AlarmReceiver.class);
  PendingIntent pendingIntent = PendingIntent.getBroadcast(getBaseContext(), RQS_1, intent, 0);
  AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
  alarmManager.set(AlarmManager.RTC_WAKEUP, targetCal.getTimeInMillis(), pendingIntent);

  if(repeat){
   alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, 
     targetCal.getTimeInMillis(), 
     TimeUnit.MINUTES.toMillis(5),
     pendingIntent);
   
   textAlarmPrompt.setText(
     "\n\n***\n"
     + "Alarm is set@ " + targetCal.getTime() + "\n"
     + "Repeat every 5 minutes\n"
     + "***\n");
  }else{
   alarmManager.set(AlarmManager.RTC_WAKEUP, 
     targetCal.getTimeInMillis(),  
     pendingIntent);
   
   textAlarmPrompt.setText(
     "\n\n***\n"
     + "Alarm is set@ " + targetCal.getTime() + "\n"
     + "One shot\n"
     + "***\n");
  }

 }
 
 private void cancelAlarm(){

  textAlarmPrompt.setText(
    "\n\n***\n"
    + "Alarm Cancelled! \n"
    + "***\n");
  
  Intent intent = new Intent(getBaseContext(), AlarmReceiver.class);
  PendingIntent pendingIntent = PendingIntent.getBroadcast(getBaseContext(), RQS_1, intent, 0);
  AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
  alarmManager.cancel(pendingIntent);

 }
  
}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <TimePicker
        android:id="@+id/picker"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <CheckBox
        android:id="@+id/optrepeat"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Repeat every 5 minutes" />
    <Button
        android:id="@+id/startSetDialog"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Set Target Time"/>
    <Button
        android:id="@+id/cancel"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Cancel Alarm"/>
    <TextView
        android:id="@+id/alarmprompt"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</LinearLayout>


Both AlarmReceiver.java and AndroidManifest.xml keep no change, refer to the exercise "Create alarm set on a specified time, using AlarmManager and BroadcastReceiver."


Download the files.


Next:
- Generate Notification using Notification.Builder for Android 3.0


Thursday, May 24, 2012

Cancel alarm with a matching PendingIntent

Call cancel(PendingIntent) method of AlarmManager to remove any alarms with a matching Intent. Any alarm, of any type, whose Intent matches this one (as defined by filterEquals(Intent)), will be canceled.

Cancel alarm with a matching PendingIntent


Work on the former exercise "Create alarm set on a specified time, using AlarmManager and BroadcastReceiver." Modify AndroidTimeActivity.java, the code to cancel alarm is in cancelAlarm().
package com.exercise.AndroidTime;

import java.util.Calendar;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TimePicker;

public class AndroidTimeActivity extends Activity {
 
 TimePicker myTimePicker;
 Button buttonstartSetDialog;
 Button buttonCancelAlarm;
 TextView textAlarmPrompt;
 
 TimePickerDialog timePickerDialog;
 
 final static int RQS_1 = 1;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        textAlarmPrompt = (TextView)findViewById(R.id.alarmprompt);
        
        buttonstartSetDialog = (Button)findViewById(R.id.startSetDialog);
        buttonstartSetDialog.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    textAlarmPrompt.setText("");
    openTimePickerDialog(false);
    
   }});

        buttonCancelAlarm = (Button)findViewById(R.id.cancel);
        buttonCancelAlarm.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    cancelAlarm();
   }});
        
    }

 private void openTimePickerDialog(boolean is24r){
  Calendar calendar = Calendar.getInstance();
  
  timePickerDialog = new TimePickerDialog(
    AndroidTimeActivity.this, 
    onTimeSetListener, 
    calendar.get(Calendar.HOUR_OF_DAY), 
    calendar.get(Calendar.MINUTE), 
    is24r);
  timePickerDialog.setTitle("Set Alarm Time");  
        
  timePickerDialog.show();

 }
    
    OnTimeSetListener onTimeSetListener
    = new OnTimeSetListener(){

  @Override
  public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

   Calendar calNow = Calendar.getInstance();
   Calendar calSet = (Calendar) calNow.clone();

   calSet.set(Calendar.HOUR_OF_DAY, hourOfDay);
   calSet.set(Calendar.MINUTE, minute);
   calSet.set(Calendar.SECOND, 0);
   calSet.set(Calendar.MILLISECOND, 0);
   
   if(calSet.compareTo(calNow) <= 0){
    //Today Set time passed, count to tomorrow
    calSet.add(Calendar.DATE, 1);
   }
   
   setAlarm(calSet);
  }};

 private void setAlarm(Calendar targetCal){

  textAlarmPrompt.setText(
    "\n\n***\n"
    + "Alarm is set@ " + targetCal.getTime() + "\n"
    + "***\n");
  
  Intent intent = new Intent(getBaseContext(), AlarmReceiver.class);
  PendingIntent pendingIntent = PendingIntent.getBroadcast(getBaseContext(), RQS_1, intent, 0);
  AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
  alarmManager.set(AlarmManager.RTC_WAKEUP, targetCal.getTimeInMillis(), pendingIntent);

 }
 
 private void cancelAlarm(){

  textAlarmPrompt.setText(
    "\n\n***\n"
    + "Alarm Cancelled! \n"
    + "***\n");
  
  Intent intent = new Intent(getBaseContext(), AlarmReceiver.class);
  PendingIntent pendingIntent = PendingIntent.getBroadcast(getBaseContext(), RQS_1, intent, 0);
  AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
  alarmManager.cancel(pendingIntent);

 }
  
}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <Button
        android:id="@+id/startSetDialog"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Set Target Time"/>
    <Button
        android:id="@+id/cancel"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Cancel Alarm"/>
    <TextView
        android:id="@+id/alarmprompt"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</LinearLayout>


Both AlarmReceiver.java and AndroidManifest.xml keep no change, refer to the exercise "Create alarm set on a specified time, using AlarmManager and BroadcastReceiver."

Download the files.

Next:
- schedule a repeating alarm


Android 4.0 Mini PC -on-a-stick

MK802 Android 4.0 Mini PC -on-a-stick: This little USB Thumb Drive-Sized Android
Computer featuring Android 4.0 and an AllWinner A10 SoC clocked at 1.5 GHz. It also includes a mini-HDMI jack to output 1080p video to an HDTV, a microSD slot for adding up to 32 GB of storage, a microUSB port for power and a USB 2.0 Host port. It is availiable on Aliexpress for 74 USD with free shipping now.

Huge Anniversary Sale!


Don't miss the last week of AliExpress' Huge Anniversary Sale! Make sure you grab amazing deals of up to 50% off thousands of items, including the hottest fashions and a wide selection of electronics including tablets, cell phones and computers. Plus, save big on bridal & formal dresses, automotive supplies, home & garden, sports equipment and more!

-----------------------------------------------------------------------------
Coupon Code: "AEMAYLHM5"
Description: $5 OFF with $100 Purchase!
Valid from 5/16/12 - 5/31/12, First Come First Served!
------------------------------------------------------------------------------------------------


$5 OFF with $100 Purchase! First-come, First-served. Code: AEMAYLHM5

Professional Android Sensor Programming



Learn to build human-interactive Android apps, starting with device sensors


This book shows Android developers how to exploit the rich set of device sensors—locational, physical (temperature, pressure, light, acceleration, etc.), cameras, microphones, and speech recognition—in order to build fully human-interactive Android applications. Whether providing hands-free directions or checking your blood pressure, Professional Android Sensor Programming shows how to turn possibility into reality.
The authors provide techniques that bridge the gap between accessing sensors and putting them to meaningful use in real-world situations. They not only show you how to use the sensor related APIs effectively, they also describe how to use supporting Android OS components to build complete systems. Along the way, they provide solutions to problems that commonly occur when using Android's sensors, with tested, real-world examples. Ultimately, this invaluable resource provides in-depth, runnable code examples that you can then adapt for your own applications.
  • Shows experienced Android developers how to exploit the rich set of Android smartphone sensors to build human-interactive Android apps
  • Explores Android locational and physical sensors (including temperature, pressure, light, acceleration, etc.), as well as cameras, microphones, and speech recognition
  • Helps programmers use the Android sensor APIs, use Android OS components to build complete systems, and solve common problems
  • Includes detailed, functional code that you can adapt and use for your own applications
  • Shows you how to successfully implement real-world solutions using each class of sensors for determining location, interpreting physical sensors, handling images and audio, and recognizing and acting on speech
Learn how to write programs for this fascinating aspect of mobile app development with Professional Android Sensor Programming.

Create alarm set on a specified time, using AlarmManager and BroadcastReceiver.


In this exercise, set alarm using AlarmManager to trigger our BroadcastReceiver on a specified time. We will use "TimePickerDialog" to set the target time.

Alarm will be triggered on a specified time


Modify main.xml to add a Button to start the TimePickerDialog to set the alarm time, add a TextView to display the set time.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <Button
        android:id="@+id/startSetDialog"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Set Target Time"/>
    <TextView
        android:id="@+id/alarmprompt"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

In the main activity, AndroidTimeActivity, the main code to start Alarm is in setAlarm(Calendar targetCal) method. Retrieve AlarmManager by through getSystemService(Context.ALARM_SERVICE). And set our alarm with alarm type(RTC_WAKEUP), trigger time, and pendingIntent. AlarmReceiver is the class of our BroadcastReceiver, it's onReceive() method will be called when alram trigger.

AndroidTimeActivity.java
package com.exercise.AndroidTime;

import java.util.Calendar;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TimePicker;

public class AndroidTimeActivity extends Activity {
 
 TimePicker myTimePicker;
 Button buttonstartSetDialog;
 TextView textAlarmPrompt;
 
 TimePickerDialog timePickerDialog;
 
 final static int RQS_1 = 1;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        textAlarmPrompt = (TextView)findViewById(R.id.alarmprompt);
        
        buttonstartSetDialog = (Button)findViewById(R.id.startSetDialog);
        buttonstartSetDialog.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    textAlarmPrompt.setText("");
    openTimePickerDialog(false);
    
   }});

    }

  
 private void openTimePickerDialog(boolean is24r){
  Calendar calendar = Calendar.getInstance();
  
  timePickerDialog = new TimePickerDialog(
    AndroidTimeActivity.this, 
    onTimeSetListener, 
    calendar.get(Calendar.HOUR_OF_DAY), 
    calendar.get(Calendar.MINUTE), 
    is24r);
  timePickerDialog.setTitle("Set Alarm Time");  
        
  timePickerDialog.show();

 }
    
    OnTimeSetListener onTimeSetListener
    = new OnTimeSetListener(){

  @Override
  public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

   Calendar calNow = Calendar.getInstance();
   Calendar calSet = (Calendar) calNow.clone();

   calSet.set(Calendar.HOUR_OF_DAY, hourOfDay);
   calSet.set(Calendar.MINUTE, minute);
   calSet.set(Calendar.SECOND, 0);
   calSet.set(Calendar.MILLISECOND, 0);
   
   if(calSet.compareTo(calNow) <= 0){
    //Today Set time passed, count to tomorrow
    calSet.add(Calendar.DATE, 1);
   }
   
   setAlarm(calSet);
  }};

 private void setAlarm(Calendar targetCal){

  textAlarmPrompt.setText(
    "\n\n***\n"
    + "Alarm is set@ " + targetCal.getTime() + "\n"
    + "***\n");
  
  Intent intent = new Intent(getBaseContext(), AlarmReceiver.class);
  PendingIntent pendingIntent = PendingIntent.getBroadcast(getBaseContext(), RQS_1, intent, 0);
  AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
  alarmManager.set(AlarmManager.RTC_WAKEUP, targetCal.getTimeInMillis(), pendingIntent);
  
 }
  
}

AlarmReceiver.java
package com.exercise.AndroidTime;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class AlarmReceiver extends BroadcastReceiver {

 @Override
 public void onReceive(Context arg0, Intent arg1) {
  Toast.makeText(arg0, "Alarm received!", Toast.LENGTH_LONG).show();

 }

}

Modify AndroidManifest.xml to add receiver ".AlarmReceiver".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.exercise.AndroidTime"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".AndroidTimeActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".AlarmReceiver" android:process=":remote" />
    </application>

</manifest>


Download the files.

Next:
- Cancel alarm with a matching PendingIntent

Related:
- Set alarm on specified date/time with DatePicker/TimePicker

Tuesday, May 22, 2012

Get the difference between two Calendar

Modify the exercise "android.app.TimePickerDialog", calculate the difference between time and next set time in TimePickerDialog.

Get the difference between two Calendar


package com.exercise.AndroidTime;

import java.util.Calendar;

import android.app.Activity;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TimePicker;

public class AndroidTimeActivity extends Activity {
 
 TimePicker myTimePicker;
 Button buttonstartSetDialog;
 TextView textPrompt;
 
 TimePickerDialog timePickerDialog;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        textPrompt = (TextView)findViewById(R.id.prompt);
        
        buttonstartSetDialog = (Button)findViewById(R.id.startSetDialog);
        buttonstartSetDialog.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    openTimePickerDialog(false);
    
   }});

    }

  
 private void openTimePickerDialog(boolean is24r){
  Calendar calendar = Calendar.getInstance();
  
  timePickerDialog = new TimePickerDialog(
    AndroidTimeActivity.this, 
    onTimeSetListener, 
    calendar.get(Calendar.HOUR_OF_DAY), 
    calendar.get(Calendar.MINUTE), 
    is24r);
  timePickerDialog.setTitle("TimePickerDialog Title");  
        timePickerDialog.setMessage("TimePickerDialog Message"); 
        
  timePickerDialog.show();

 }
    
    OnTimeSetListener onTimeSetListener
    = new OnTimeSetListener(){

  @Override
  public void onTimeSet(TimePicker view, int hourOfDay, int minute) {

   Calendar calNow = Calendar.getInstance();
   Calendar calSet = (Calendar) calNow.clone();

   calSet.set(Calendar.HOUR_OF_DAY, hourOfDay);
   calSet.set(Calendar.MINUTE, minute);
   
   String stringPrompt = "Current Time is: " + calNow.getTime() + " / " + calNow.getTimeInMillis() + "\n"
     + "Set Time is: " + calSet.getTime() + " / " + calSet.getTimeInMillis() + "\n"
     + "calSet.compareTo(calNow) = " + calSet.compareTo(calNow) + "\n\n";
   
   if(calSet.compareTo(calNow) > 0){
    //Today Set time not yet passed
    long diff = calSet.getTimeInMillis() - calNow.getTimeInMillis();
    stringPrompt += "Millis to set time = " + diff;
   }else{
    //Today Set time passed, count to tomorrow
    calSet.add(Calendar.DATE, 1);
    long diff = calSet.getTimeInMillis() - calNow.getTimeInMillis();
    stringPrompt += "Millis to tomorrow set time = " + diff;
   }
   
   textPrompt.setText(stringPrompt);
  }};

}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <Button
        android:id="@+id/startSetDialog"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Set Target Time"/>
    <TextView
        android:id="@+id/prompt"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
</LinearLayout>


Related:
- java.util.Calendar


java.util.Calendar

java.util.Calendar is an abstract base class for converting between a Date object and a set of integer fields such as YEAR, MONTH, DAY, HOUR, and so on.

Example of usage of Calendar.

Calendar


package com.exercise.AndroidCalendar;

import java.util.Calendar;
import java.util.Locale;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;

public class AndroidCalendarActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        TextView prompt = new TextView(this);
        setContentView(prompt);
        prompt.setText(getCalendarPrompt());
    }
    
    private String getCalendarPrompt(){
     Calendar rightNow = Calendar.getInstance();
     
     String p = rightNow + "\n\n"
       + rightNow.get(Calendar.YEAR) + "-"
       + rightNow.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.US) + "-"
       + rightNow.get(Calendar.DATE) + "\n"
       + "DAY_OF_YEAR: " + rightNow.get(Calendar.DAY_OF_YEAR) + "\n"
       + "DAY_OF_MONTH: " + rightNow.get(Calendar.DAY_OF_MONTH) + "\n"
       + "DAY_OF_WEEK: " + rightNow.get(Calendar.DAY_OF_WEEK) + "\n"
       + "\n"
       + "getTime(): " + rightNow.getTime() + "\n"
       + "getTimeInMillis(): " + rightNow.getTimeInMillis() + "\n"
       + "\n";
     
     return p;
    }
}


Related:
- Get the difference between two Calendar


Monday, May 21, 2012

The Google I/O Countdown Contest



Android Power is launching a huge giveaway that'll take us all the way up to the big event - Google I/O. You'll have a chance to score awesome Android prizes each week from now through the start of I/O -- and from high-end tablets to standout Android swag, there's plenty of cool stuff just waiting to be won.

Details: The Google I/O Countdown Contest: Win a free Toshiba Excite 10 tablet!


android.app.TimePickerDialog

android.app.TimePickerDialog is dialog that prompts the user for the time of day using a TimePicker.

TimePickerDialog


Note:
  • If the TimePickerDialog closed by "Set" button, OnTimeSetListener and OnDismissListener will be called.
  • If the TimePickerDialog closed by "Cancel" button, only OnDismissListener will be called.
  • If the TimePickerDialog closed by "BACK" system button, OnCancelListener and OnDismissListener will be called.

package com.exercise.AndroidTime;

import java.util.Calendar;

import android.app.Activity;
import android.app.TimePickerDialog;
import android.app.TimePickerDialog.OnTimeSetListener;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.DialogInterface.OnShowListener;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TimePicker;

public class AndroidTimeActivity extends Activity {
 
 TimePicker myTimePicker;
 Button buttonStartDialog12, buttonStartDialog24;
 TextView info;
 
 TimePickerDialog timePickerDialog;
 
 String dialogMsg;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        info = (TextView)findViewById(R.id.info);
        
        buttonStartDialog12 = (Button)findViewById(R.id.startDialog12);
        buttonStartDialog12.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    openTimePickerDialog(false);
    
   }});
        
        buttonStartDialog24 = (Button)findViewById(R.id.startDialog24);
        buttonStartDialog24.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    openTimePickerDialog(true);
    
   }});
    }

  
 private void openTimePickerDialog(boolean is24r){
  Calendar calendar = Calendar.getInstance();
  timePickerDialog = new TimePickerDialog(
    AndroidTimeActivity.this, 
    onTimeSetListener, 
    calendar.get(Calendar.HOUR_OF_DAY), 
    calendar.get(Calendar.MINUTE), 
    is24r);
  timePickerDialog.setTitle("TimePickerDialog Title");  
        timePickerDialog.setMessage("TimePickerDialog Message"); 
        
        timePickerDialog.setOnShowListener(new OnShowListener(){

   @Override
   public void onShow(DialogInterface arg0) {
    dialogMsg = "OnShow\n";
    info.setText(dialogMsg);
   }});
        
        timePickerDialog.setOnCancelListener(new OnCancelListener(){

   @Override
   public void onCancel(DialogInterface dialog) {
    dialogMsg += "OnCancel\n";
    info.setText(dialogMsg);
    
   }});
        timePickerDialog.setOnDismissListener(new OnDismissListener(){

   @Override
   public void onDismiss(DialogInterface arg0) {
    dialogMsg += "OnDismiss\n";
    info.setText(dialogMsg);
   }});
        
  timePickerDialog.show();

 }
    
    OnTimeSetListener onTimeSetListener
    = new OnTimeSetListener(){

  @Override
  public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
   dialogMsg += "OnTimeSet " + hourOfDay + " : " + minute + "\n";
   info.setText(dialogMsg);
  }};

}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <Button
        android:id="@+id/startDialog12"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Start TimePickerDialog in AM/PM mode "/>
    <Button
        android:id="@+id/startDialog24"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Start TimePickerDialog in 24hr mode "/>
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" 
        android:gravity="bottom"
        >
    <TextView
        android:id="@+id/info"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
 </LinearLayout>
</LinearLayout>


Related:
- android.widget.TimePicker


Sunday, May 20, 2012

android.widget.TimePicker


android.widget.TimePicker provide a special view for selecting the time of day, in either 24 hour or AM/PM mode. The hour, each minute digit, and AM/PM (if applicable) can be conrolled by vertical spinners. The hour can be entered by keyboard input. Entering in two digit hours can be accomplished by hitting two digits within a timeout of about a second (e.g. '1' then '2' to select 12). The minutes can be entered by entering single digits. Under AM/PM mode, the user can hit 'a', 'A", 'p' or 'P' to pick.

TimePicker


It can be set 12/24 hour mode by calling setIs24HourView() method. And also can be checked by calling it's is24HourView() method.

We can retrieve the hour and minute set by calling getCurrentHour() and getCurrentMinute() methods.

We can also implement our OnTimeChangedListener, it will be called when the time has been adjusted. - BUT, it will not be called if AM/PM button (in 12 hour mode) changed. May be it's a bug!

package com.exercise.AndroidTime;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.TimePicker;
import android.widget.TimePicker.OnTimeChangedListener;

public class AndroidTimeActivity extends Activity {
 
 TimePicker myTimePicker;
 Button buttonToggle24Hour, buttonGetTime;
 TextView infoByPicker, infoByButton;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        myTimePicker = (TimePicker)findViewById(R.id.picker);
        buttonToggle24Hour = (Button)findViewById(R.id.toggle24Hour);
        buttonGetTime = (Button)findViewById(R.id.gettime);
        infoByPicker = (TextView)findViewById(R.id.infoByPicker);
        infoByButton = (TextView)findViewById(R.id.infoByButton);

        myTimePicker.setOnTimeChangedListener(onTimeChangedListener);
        buttonGetTime.setOnClickListener(buttonGetTimeClickListener);
        
        buttonToggle24Hour.setOnClickListener(buttonToggle24HourClickListener);
    }
    
    OnTimeChangedListener onTimeChangedListener
    = new OnTimeChangedListener(){

  @Override
  public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
   infoByPicker.setText(
     "Triggered by TimePicker.setOnTimeChangedListener: \n"
     + hourOfDay 
     + " : " 
     + minute + "\n");
   
  }};
    
    Button.OnClickListener buttonGetTimeClickListener
    = new Button.OnClickListener(){

  @Override
  public void onClick(View v) {
   infoByButton.setText(
     "Triggered by Button: \n"
     + myTimePicker.getCurrentHour() 
     + " : " 
     + myTimePicker.getCurrentMinute() + "\n");
   
  }
     
    };
    
    Button.OnClickListener buttonToggle24HourClickListener
    = new Button.OnClickListener(){

  @Override
  public void onClick(View v) {
   myTimePicker.setIs24HourView(!myTimePicker.is24HourView());
  }
     
    };
}


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <TimePicker
        android:id="@+id/picker"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/toggle24Hour"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Toggle 12/24 Hour "/>
    <Button
        android:id="@+id/gettime"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Get Time"/>
    <TextView
        android:id="@+id/infoByPicker"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/infoByButton"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>


Related:
- android.app.TimePickerDialog


Thursday, May 17, 2012

Implement OnItemGestureListener on OpenStreetMap to detect user touch and retrieve touched items properties


Modify the code in last exercise "Create multi-marker OpenStreetMap for Android", add OnItemGestureListener for our ItemizedIconOverlay. Such that the OpenStreetMapView can react user touch. And also it can retrieve the touched item from the passed parameters. Then pass OnItemGestureListener to ItemizedIconOverlay in constructor.

Implement OnItemGestureListener on OpenStreetMap to detect user touch and retrieve touched items properties


package com.exercise.OpenStreetMapView;

import java.util.ArrayList;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapController;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.ItemizedIconOverlay;
import org.osmdroid.views.overlay.ItemizedIconOverlay.OnItemGestureListener;
import org.osmdroid.views.overlay.OverlayItem;
import org.osmdroid.views.overlay.ScaleBarOverlay;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;

public class AndroidOpenStreetMapViewActivity extends Activity {
 
 private MapView myOpenMapView;
 private MapController myMapController;
 
 ArrayList<OverlayItem> anotherOverlayItemArray;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        myOpenMapView = (MapView)findViewById(R.id.openmapview);
        myOpenMapView.setBuiltInZoomControls(true);
        myMapController = myOpenMapView.getController();
        myMapController.setZoom(2);
      
        //--- Create Another Overlay for multi marker
        anotherOverlayItemArray = new ArrayList<OverlayItem>();
        anotherOverlayItemArray.add(new OverlayItem(
          "0, 0", "0, 0", new GeoPoint(0, 0)));
        anotherOverlayItemArray.add(new OverlayItem(
          "US", "US", new GeoPoint(38.883333, -77.016667)));
        anotherOverlayItemArray.add(new OverlayItem(
          "China", "China", new GeoPoint(39.916667, 116.383333)));
        anotherOverlayItemArray.add(new OverlayItem(
          "United Kingdom", "United Kingdom", new GeoPoint(51.5, -0.116667)));
        anotherOverlayItemArray.add(new OverlayItem(
          "Germany", "Germany", new GeoPoint(52.516667, 13.383333)));
        anotherOverlayItemArray.add(new OverlayItem(
          "Korea", "Korea", new GeoPoint(38.316667, 127.233333)));
        anotherOverlayItemArray.add(new OverlayItem(
          "India", "India", new GeoPoint(28.613333, 77.208333)));
        anotherOverlayItemArray.add(new OverlayItem(
          "Russia", "Russia", new GeoPoint(55.75, 37.616667)));
        anotherOverlayItemArray.add(new OverlayItem(
          "France", "France", new GeoPoint(48.856667, 2.350833)));
        anotherOverlayItemArray.add(new OverlayItem(
          "Canada", "Canada", new GeoPoint(45.4, -75.666667)));
        
        ItemizedIconOverlay<OverlayItem> anotherItemizedIconOverlay 
         = new ItemizedIconOverlay<OverlayItem>(
           this, anotherOverlayItemArray, myOnItemGestureListener);
        myOpenMapView.getOverlays().add(anotherItemizedIconOverlay);
        //---
        
        //Add Scale Bar
        ScaleBarOverlay myScaleBarOverlay = new ScaleBarOverlay(this);
        myOpenMapView.getOverlays().add(myScaleBarOverlay);
    }
    
    OnItemGestureListener<OverlayItem> myOnItemGestureListener
    = new OnItemGestureListener<OverlayItem>(){

  @Override
  public boolean onItemLongPress(int arg0, OverlayItem arg1) {
   // TODO Auto-generated method stub
   return false;
  }

  @Override
  public boolean onItemSingleTapUp(int index, OverlayItem item) {
   Toast.makeText(AndroidOpenStreetMapViewActivity.this, 
     item.mDescription + "\n"
     + item.mTitle + "\n"
     + item.mGeoPoint.getLatitudeE6() + " : " + item.mGeoPoint.getLongitudeE6(), 
     Toast.LENGTH_LONG).show();
   return true;
  }
     
    };
   
}


Download the files.

Next:
- Add MyLocationOverlay on OpenStreetMap, to display user location and compass.


Create multi-marker OpenStreetMap for Android

A example to craete Android app using OpenStreetMap with multi-marker.

multi-marker OpenStreetMap on Android


AndroidOpenStreetMapViewActivity.java
package com.exercise.OpenStreetMapView;

import java.util.ArrayList;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapController;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.ItemizedIconOverlay;
import org.osmdroid.views.overlay.OverlayItem;
import org.osmdroid.views.overlay.ScaleBarOverlay;

import android.app.Activity;
import android.os.Bundle;

public class AndroidOpenStreetMapViewActivity extends Activity {
 
 private MapView myOpenMapView;
 private MapController myMapController;
 
 ArrayList<OverlayItem> anotherOverlayItemArray;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        myOpenMapView = (MapView)findViewById(R.id.openmapview);
        myOpenMapView.setBuiltInZoomControls(true);
        myMapController = myOpenMapView.getController();
        myMapController.setZoom(2);
      
        //--- Create Another Overlay for multi marker
        anotherOverlayItemArray = new ArrayList<OverlayItem>();
        anotherOverlayItemArray.add(new OverlayItem(
          "0, 0", "0, 0", new GeoPoint(0, 0)));
        anotherOverlayItemArray.add(new OverlayItem(
          "US", "US", new GeoPoint(38.883333, -77.016667)));
        anotherOverlayItemArray.add(new OverlayItem(
          "China", "China", new GeoPoint(39.916667, 116.383333)));
        anotherOverlayItemArray.add(new OverlayItem(
          "United Kingdom", "United Kingdom", new GeoPoint(51.5, -0.116667)));
        anotherOverlayItemArray.add(new OverlayItem(
          "Germany", "Germany", new GeoPoint(52.516667, 13.383333)));
        anotherOverlayItemArray.add(new OverlayItem(
          "Korea", "Korea", new GeoPoint(38.316667, 127.233333)));
        anotherOverlayItemArray.add(new OverlayItem(
          "India", "India", new GeoPoint(28.613333, 77.208333)));
        anotherOverlayItemArray.add(new OverlayItem(
          "Russia", "Russia", new GeoPoint(55.75, 37.616667)));
        anotherOverlayItemArray.add(new OverlayItem(
          "France", "France", new GeoPoint(48.856667, 2.350833)));
        anotherOverlayItemArray.add(new OverlayItem(
          "Canada", "Canada", new GeoPoint(45.4, -75.666667)));
        
        ItemizedIconOverlay<OverlayItem> anotherItemizedIconOverlay 
         = new ItemizedIconOverlay<OverlayItem>(
           this, anotherOverlayItemArray, null);
        myOpenMapView.getOverlays().add(anotherItemizedIconOverlay);
        //---
        
        //Add Scale Bar
        ScaleBarOverlay myScaleBarOverlay = new ScaleBarOverlay(this);
        myOpenMapView.getOverlays().add(myScaleBarOverlay);
    }
    
}

main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
 <org.osmdroid.views.MapView
     android:id="@+id/openmapview"
     android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
     
</LinearLayout>

Permission needed in AndroidManifest.xml
  • android.permission.ACCESS_NETWORK_STATE
  • android.permission.INTERNET
  • android.permission.WRITE_EXTERNAL_STORAGE
  • android.permission.ACCESS_FINE_LOCATION


Download the files.

Related:
- Prepare Java Build Path to osmdroid and slf4j-android libs for OpenStreetMapView
- Display current location marker on OpenStreetMap

Next:
- Implement OnItemGestureListener on OpenStreetMap to detect user touch and retrieve touched items properties


Wednesday, May 16, 2012

Display current location marker on OpenStreetMap


Further works on last exercise "Add Scale Bar on OpenStreetMap", add a marker on OpenStreetMap, to indicate the current location.

Display current location marker on OpenStreetMap


Copy your marker drawing in the folder /res/drawable/. You can find number of Android icons in installed SDK directory: /android-sdks/platforms/<android version>/data/res/

overlayItemArray is a array of OverlayItem, it have none or ONE item of current location only.

In this exercise, create custom MyItemizedIconOverlay class extends from MyItemizedIconOverlay. Then add the MyItemizedIconOverlay object (myItemizedIconOverlay) to the MapView overlay. Refer to the code below the comment "//--- Create Overlay".

To update the item(s) in overlayItemArray, simple use methods of clear(), add()...etc. Refer to the method setOverlayLoc(Location overlayloc).

In the custom ItemizedIconOverlay class, MyItemizedIconOverlay; I override onSingleTapUp() method to return true always - Because the app will FORCE CLOSE without it! (I don't know why at this moment)

Modify AndroidOpenStreetMapViewActivity.java. Other files refer to previous exercises.

package com.exercise.OpenStreetMapView;

import java.util.ArrayList;
import java.util.List;

import org.osmdroid.DefaultResourceProxyImpl;
import org.osmdroid.ResourceProxy;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapController;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.ItemizedIconOverlay;
import org.osmdroid.views.overlay.OverlayItem;
import org.osmdroid.views.overlay.ScaleBarOverlay;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Point;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.view.MotionEvent;

public class AndroidOpenStreetMapViewActivity extends Activity {
 
 private MapView myOpenMapView;
 private MapController myMapController;
 
 LocationManager locationManager;
 
 ArrayList<OverlayItem> overlayItemArray;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        myOpenMapView = (MapView)findViewById(R.id.openmapview);
        myOpenMapView.setBuiltInZoomControls(true);
        myMapController = myOpenMapView.getController();
        myMapController.setZoom(12);
        

        //--- Create Overlay
        overlayItemArray = new ArrayList<OverlayItem>();
        
        DefaultResourceProxyImpl defaultResourceProxyImpl 
         = new DefaultResourceProxyImpl(this);
        MyItemizedIconOverlay myItemizedIconOverlay 
         = new MyItemizedIconOverlay(
           overlayItemArray, null, defaultResourceProxyImpl);
        myOpenMapView.getOverlays().add(myItemizedIconOverlay);
        //---

        
        locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
        
        //for demo, getLastKnownLocation from GPS only, not from NETWORK
        Location lastLocation 
         = locationManager.getLastKnownLocation(
           LocationManager.GPS_PROVIDER);
        if(lastLocation != null){
         updateLoc(lastLocation);
        }
        
        //Add Scale Bar
        ScaleBarOverlay myScaleBarOverlay = new ScaleBarOverlay(this);
        myOpenMapView.getOverlays().add(myScaleBarOverlay);
    }
    
    @Override
 protected void onResume() {
  // TODO Auto-generated method stub
  super.onResume();
  locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, myLocationListener);
  locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, myLocationListener);
 }

 @Override
 protected void onPause() {
  // TODO Auto-generated method stub
  super.onPause();
  locationManager.removeUpdates(myLocationListener);
 }

 private void updateLoc(Location loc){
     GeoPoint locGeoPoint = new GeoPoint(loc.getLatitude(), loc.getLongitude());
     myMapController.setCenter(locGeoPoint);
     
     setOverlayLoc(loc);
     
     myOpenMapView.invalidate();
    }
 
 private void setOverlayLoc(Location overlayloc){
  GeoPoint overlocGeoPoint = new GeoPoint(overlayloc);
  //---
     overlayItemArray.clear();
     
     OverlayItem newMyLocationItem = new OverlayItem(
       "My Location", "My Location", overlocGeoPoint);
     overlayItemArray.add(newMyLocationItem);
     //---
 }
    
    private LocationListener myLocationListener
    = new LocationListener(){

  @Override
  public void onLocationChanged(Location location) {
   // TODO Auto-generated method stub
   updateLoc(location);
  }

  @Override
  public void onProviderDisabled(String provider) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onProviderEnabled(String provider) {
   // TODO Auto-generated method stub
   
  }

  @Override
  public void onStatusChanged(String provider, int status, Bundle extras) {
   // TODO Auto-generated method stub
   
  }
     
    };
    
    private class MyItemizedIconOverlay extends ItemizedIconOverlay<OverlayItem>{

  public MyItemizedIconOverlay(
    List<OverlayItem> pList,
    org.osmdroid.views.overlay.ItemizedIconOverlay.OnItemGestureListener<OverlayItem> pOnItemGestureListener,
    ResourceProxy pResourceProxy) {
   super(pList, pOnItemGestureListener, pResourceProxy);
   // TODO Auto-generated constructor stub
  }

  @Override
  public void draw(Canvas canvas, MapView mapview, boolean arg2) {
   // TODO Auto-generated method stub
   super.draw(canvas, mapview, arg2);
   
   if(!overlayItemArray.isEmpty()){
    
    //overlayItemArray have only ONE element only, so I hard code to get(0)
    GeoPoint in = overlayItemArray.get(0).getPoint();
    
    Point out = new Point();
    mapview.getProjection().toPixels(in, out);
    
    Bitmap bm = BitmapFactory.decodeResource(getResources(), 
      R.drawable.ic_menu_mylocation);
    canvas.drawBitmap(bm, 
      out.x - bm.getWidth()/2,  //shift the bitmap center
      out.y - bm.getHeight()/2,  //shift the bitmap center
      null);
   }
  }

  @Override
  public boolean onSingleTapUp(MotionEvent event, MapView mapView) {
   // TODO Auto-generated method stub
   //return super.onSingleTapUp(event, mapView);
   return true;
  }
    }
}


Download the files.

Next:
- Create multi-marker OpenStreetMap for Android