Thursday, April 30, 2015

Example of using AlarmManager to trigger BroadcastReceiver / alarmManager.set() and setExact()

A simple example of using AlarmManager to trigger a BroadcastReceiver to be called in 10 seconds later.


Note: Beginning with API 19 (KITKAT) alarm delivery is inexact: the OS will shift alarms in order to minimize wakeups and battery use. There are new APIs to support applications which need strict delivery guarantees; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent). Applications whose targetSdkVersion is earlier than API 19 will continue to see the previous behavior in which all alarms are delivered exactly when requested.

reference: http://developer.android.com/reference/android/app/AlarmManager.html

This example also show how to determine calling set() or setExact() depends on Build.VERSION.SDK_INT. But no demo how in-exact with calling set(), because  alarms scheduled in the near future will not be deferred as long as alarms scheduled far in the future.

AlarmReceiver.java
package com.example.androidalarm;

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 intent) {
  
  Toast.makeText(context, 
   "AlarmReceiver.onReceive()", 
   Toast.LENGTH_LONG).show();
 }

}

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidalarm.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <Chronometer
        android:id="@+id/chronometer"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal" />

    <Button
        android:id="@+id/setnocheck"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Set Alarm 10 sec later - alarmManager.set()" />
    
    <Button
        android:id="@+id/setwithversioncheck"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Set Alarm 10 sec later - with SDK Version check" />

</LinearLayout>

MainActivity.java
package com.example.androidalarm;

import java.util.Calendar;
import android.support.v7.app.ActionBarActivity;
import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Chronometer;
import android.widget.Toast;


public class MainActivity extends ActionBarActivity {
 
 Chronometer chronometer;
 Button btnSetNoCheck, btnSetWithVerCheck;
 final static int RQS_1 = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        chronometer = (Chronometer)findViewById(R.id.chronometer);
        btnSetNoCheck = (Button)findViewById(R.id.setnocheck);
        btnSetNoCheck.setOnClickListener(onClickListener);
        btnSetWithVerCheck = (Button)findViewById(R.id.setwithversioncheck);
        btnSetWithVerCheck.setOnClickListener(onClickListener);
    }
    
    @SuppressLint("NewApi") 
    OnClickListener onClickListener = new OnClickListener(){

  @Override
  public void onClick(View v) {
   chronometer.setBase(SystemClock.elapsedRealtime());
      chronometer.start();
      
      //10 seconds later
      Calendar cal = Calendar.getInstance();
      cal.add(Calendar.SECOND, 10);
      
      Intent intent = new Intent(getBaseContext(), AlarmReceiver.class);
      PendingIntent pendingIntent = 
       PendingIntent.getBroadcast(getBaseContext(), 
         RQS_1, intent, PendingIntent.FLAG_ONE_SHOT);
      AlarmManager alarmManager = 
       (AlarmManager)getSystemService(Context.ALARM_SERVICE);
      
      if(v==btnSetNoCheck){
       alarmManager.set(AlarmManager.RTC_WAKEUP, 
        cal.getTimeInMillis(), pendingIntent);
       Toast.makeText(getBaseContext(), 
        "call alarmManager.set()", 
        Toast.LENGTH_LONG).show();
      }else{
       if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
       {
        alarmManager.set(AlarmManager.RTC_WAKEUP, 
         cal.getTimeInMillis(), pendingIntent);
        Toast.makeText(getBaseContext(), 
         "call alarmManager.set()", 
         Toast.LENGTH_LONG).show();
       }else{
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, 
         cal.getTimeInMillis(), pendingIntent);
        Toast.makeText(getBaseContext(), 
         "call alarmManager.setExact()", 
         Toast.LENGTH_LONG).show();
       }
      }
      
  }
     
    };

}

Need to modify AndroidManifest.xml to add <receiver android:name=".AlarmReceiver" android:process=":remote" />
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidalarm"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="22" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            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 filesDownload the files.

Wednesday, April 22, 2015

Async load image from internet to ListView

Android example to load images in AsyncTask from internet to ListView.


MainActivity.java
package com.example.androidasynclist;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;

import android.support.v7.app.ActionBarActivity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {

 final static String src[] = {
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh45-l11b9CxlWURRdH3KtwaQYMf7lvCjGjhbao52Wi5r7qrd0Xfth6xTflusBxKTwJe8172h0klxldaYUJESZzyz5prDEKN2dtHrH4IaD-9OTdx1CSkein7e0FMVFBv4WVw4COAQx5vD4/s1600/_01.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggAIxdysN9SF1x7kU-8qHa3-ut75IlMneUrZj008Rnw1uZoRt8cEc08GNuBOrH1oYvERfoGKANoo1GVVAEQMOcYUt3THG0WJXl0nUyv1pYyot_fLYTfBpOvIU4LgKrRdqKbcjNpDAqZjk/s1600/_02.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicQXPrH8ePv6XTHC-gVrwt5IODdKqiaoZUiJpDZBOeKbKt1zS1deLLOIdpxLYCXCnuXTIP2ZaCFsLY49sDOmda6-S6kHJBaCrbzNlsXornSIPOW3J__TaAYj4mN6kniZIHnKlOD7XSRzE/s1600/_03.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibJPrKh6gadVz_Lk9WB88l2h1eFjXzwzltzT3qawdhx-whIker16zNLjjdOlZK6ZQb-g-Gh8f6KQgKNz3LEM6lV_uTfRjB_Jqyijx0UhXCDiBk2PmGeG9JepX3SKtG9smYIgCxoz6YBcg/s1600/_04.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJYw1j_8u6OyPr-X5kxKghanaf4uZgWykHGnR4T82FJ8X8KeVcZFT91aNR-mcUKYh8_VW4g6bC_VwskODH9yEu4Q3S_0bdtzx7fixquKWv0BVJGILN-b0vpvXGerWTPf-H6xqtmHPAq74/s1600/_05.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisebiSsu0boJ3EncEPMYnLj8MEvb2amGp0W4jrvTz-htJos8CHlUjtqALhC3Z1tS73zY1cKDn14WDJ0qqsIEova5rGxtKqfGw4jaPAWpUpWlM7OWrQrTthmZuXDlvONPUP_ixqypr1R5Y/s1600/_06.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxZwuI5jNXioK2PBfvFkhcyNj-qGTmnB8_pXATaGl5VwKWitoWAbe_OecmK-LimzJ95x_YmYjgrOY2xCLr9ScwzOalPC_1wx1l3rWvN-SKkYinS-LjQoei6OiuCA5Vhqz5XjEwrCfFv4A/s1600/_07.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhN_NWFc1tGg7yOlyj_ytqlzlAeUyjL-EOehYIKwpjqIZFdpiFxFK292j5Nl2Nercsb0JVcMGk0BNJ_7DwShmOhzbtAOreQBxXV5Kf-PnxizNC-g-relCv2ojBgAHJHIRG_kE0-B1xVx2I/s1600/_08.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvnZ4Mucl5g31ikF1BFvcPP4L2ligLXcw71xjpTIgpgbw9ycf0kGWC_dE6asoXoQq4BOY-mdKQoe6dCUpTca5jMM2BBTjigDqbvvePkhx4hPkmHvhm7HlgYj5UMGZHmBbSCrleitJ2ekQ/s1600/_09.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM-Pv79aw3p7_5zjgN08vWs6_qx7QHGxVX9kofXoAoShjrURoMNk10AgH5intycrEB6o4nb215IqkeAUGjo7A8aBRFUe_052Vmn5cY7rIYyaxFCVbeJTADl0lYj4kD71t2psHKYAsk3Uw/s1600/_10.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQAPsubpaTgVDk1vkG8Ktml3nljEBbRfva7J3hqQ1QfJZ1wdKp9UIbm8-JNyCeHwmPFEGpZ5GEih6Zgn9giK3Gcfbka8Sbqfj-0VqvCUzu7w3JbZ6QTMwGmf10zil_xeDz0p6zPopN0nY/s1600/_11.png",
   "https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidlI97E_km-6-Yx-xqrccSG5TNH5e_RU-NTOesxP1SCQMMf2ft1TQrwIA8MnapXjPrLDuzaJDzD4Eni11C7dqTJAY37LympC128pGE0OyBSZzRhyphenhyphenPYxovNdmI_JgP8l0B5dVcoK_txhy4/s1600/_12.png" };

 ListView imageList;

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

  imageList = (ListView) findViewById(R.id.imagelist);
  ArrayList<String> srcList = new ArrayList<String>(Arrays.asList(src));
  imageList.setAdapter(new CustomListAdapter(this, srcList));
 }

 // ----------------------------------------------------

 public class CustomListAdapter extends BaseAdapter {
  private ArrayList<String> listData;
  private LayoutInflater layoutInflater;

  public CustomListAdapter(Context context, ArrayList<String> listData) {
   this.listData = listData;
   layoutInflater = LayoutInflater.from(context);
  }

  @Override
  public int getCount() {
   return listData.size();
  }

  @Override
  public Object getItem(int position) {
   return listData.get(position);
  }

  @Override
  public long getItemId(int position) {
   return position;
  }

  public View getView(int position, View convertView, ViewGroup parent) {
   ViewHolder holder;
   if (convertView == null) {
    convertView = layoutInflater.inflate(R.layout.row, null);
    holder = new ViewHolder();
    holder.icon = (ImageView)convertView.findViewById(R.id.icon);
    holder.text = (TextView)convertView.findViewById(R.id.text);
    convertView.setTag(holder);
   } else {
    holder = (ViewHolder) convertView.getTag();
   }
   
   holder.text.setText(String.valueOf(position));

   if (holder.icon != null) {
    new BitmapWorkerTask(holder.icon).execute(listData.get(position));
   }
   return convertView;
  }

  class ViewHolder {
   ImageView icon;
   TextView text;
  }
 }

 // ----------------------------------------------------
 // Load bitmap in AsyncTask
 // ref:
 // http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
 class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
  private final WeakReference<ImageView> imageViewReference;
  private String imageUrl;

  public BitmapWorkerTask(ImageView imageView) {
   // Use a WeakReference to ensure the ImageView can be garbage
   // collected
   imageViewReference = new WeakReference<ImageView>(imageView);
  }

  // Decode image in background.
  @Override
  protected Bitmap doInBackground(String... params) {
   imageUrl = params[0];
   return LoadImage(imageUrl);
  }

  // Once complete, see if ImageView is still around and set bitmap.
  @Override
  protected void onPostExecute(Bitmap bitmap) {
   if (imageViewReference != null && bitmap != null) {
    final ImageView imageView = imageViewReference.get();
    if (imageView != null) {
     imageView.setImageBitmap(bitmap);
    }
   }
  }

  private Bitmap LoadImage(String URL) {
   Bitmap bitmap = null;
   InputStream in = null;
   try {
    in = OpenHttpConnection(URL);
    bitmap = BitmapFactory.decodeStream(in);
    in.close();
   } catch (IOException e1) {
   }
   return bitmap;
  }

  private InputStream OpenHttpConnection(String strURL)
    throws IOException {
   InputStream inputStream = null;
   URL url = new URL(strURL);
   URLConnection conn = url.openConnection();

   try {
    HttpURLConnection httpConn = (HttpURLConnection) conn;
    httpConn.setRequestMethod("GET");
    httpConn.connect();

    if (httpConn.getResponseCode() == HttpURLConnection.HTTP_OK) {
     inputStream = httpConn.getInputStream();
    }
   } catch (Exception ex) {
   }
   return inputStream;
  }
 }
}

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidasynclist.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <ListView
        android:id="@+id/imagelist"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

/res/layout/row.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <ImageView
        android:id="@+id/icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

Permission of "android.permission.INTERNET" is needed in AndroidManifest.xml.



download filesDownload the files.

Related:
Load WebP from Internet and display in ListView

Tuesday, April 21, 2015

Save Bitmap to storage

This example show how to save bitmap to storage (SD Card) as file, and insert to MediaStore.


As a example, the bitmap is retrieved from ImageView by calling ((BitmapDrawable)imageView.getDrawable()).getBitmap(), note that it's not always work, depends on how to load image to ImageVIew.

To save file to external storage, permission of "android.permission.WRITE_EXTERNAL_STORAGE" is needed in AndroidManifest.xml. Otherwise you cannot save the file, but no error reported.

In this example:
- The first button save the bitmap as file, with specified path. In this case, MediaStore may not know there are new image, such that may be you cannot view it in Android Gallery App or Photos App.
- The second button insert the bitmap to MediaStore by calling MediaStore.Images.Media.insertImage(ContentResolver cr, Bitmap source, String title, String description), to MediaStore path. Such that you can view it in Android Gallery App or Photos App.
- The third button save the bitmap as file, then insert the file to MediaStore by calling MediaStore.Images.Media.insertImage(ContentResolver cr, String imagePath, String name, String description). In this case, you save two files actually.


package com.example.androidsavebitmap;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import android.support.v7.app.ActionBarActivity;
import android.content.ContentResolver;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

/*
 * "android.permission.WRITE_EXTERNAL_STORAGE" is needed
 * otherwise, cannot save but no error report
 */

public class MainActivity extends ActionBarActivity {
 
 ImageView imageView;
 Button btnSaveExternalStorageDirectory;
 Button btnSaveMediaStore;
 Button btnSaveFileAndMediaStore;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView)findViewById(R.id.image);
        
        btnSaveExternalStorageDirectory = (Button)findViewById(R.id.saveExternalStorageDirectory);
        btnSaveExternalStorageDirectory.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    /*
     * Save bitmap to ExternalStorageDirectory
     */
    
    //get bitmap from ImageVIew
    //not always valid, depends on your drawable
    Bitmap bitmap = ((BitmapDrawable)imageView.getDrawable()).getBitmap();

    //always save as 
       String fileName = "test.jpg";
       
       ByteArrayOutputStream bytes = new ByteArrayOutputStream();
       bitmap.compress(Bitmap.CompressFormat.JPEG, 40, bytes);

    File ExternalStorageDirectory = Environment.getExternalStorageDirectory();
    File file = new File(ExternalStorageDirectory + File.separator + fileName);
    
    FileOutputStream fileOutputStream = null;
    try {
     file.createNewFile();
     fileOutputStream = new FileOutputStream(file);
     fileOutputStream.write(bytes.toByteArray());
     
     Toast.makeText(MainActivity.this, 
       file.getAbsolutePath(), 
       Toast.LENGTH_LONG).show();
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    } finally {
     if(fileOutputStream != null){
      try {
       fileOutputStream.close();
      } catch (IOException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }
     }
    }

   }});
        
        btnSaveMediaStore = (Button)findViewById(R.id.saveMediaStore);
        btnSaveMediaStore.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    /*
     * Save bitmap to MediaStore
     */
    
    //get bitmap from ImageVIew
    //not always valid, depends on your drawable
    Bitmap bitmap = ((BitmapDrawable)imageView.getDrawable()).getBitmap();

    ContentResolver cr = getContentResolver();
    String title = "myBitmap";
    String description = "My bitmap created by Android-er";
    String savedURL = MediaStore.Images.Media
      .insertImage(cr, bitmap, title, description);
    
    Toast.makeText(MainActivity.this, 
      savedURL, 
      Toast.LENGTH_LONG).show();
    
   }});
        
        btnSaveFileAndMediaStore = (Button)findViewById(R.id.saveExternalStorageDirectoryMediaStore);
        btnSaveFileAndMediaStore.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    /*
     * Save bitmap to ExternalStorageDirectory
     */
    
    //get bitmap from ImageVIew
    //not always valid, depends on your drawable
    Bitmap bitmap = ((BitmapDrawable)imageView.getDrawable()).getBitmap();

    //always save as 
       String fileName = "test.jpg";
       
       ByteArrayOutputStream bytes = new ByteArrayOutputStream();
       bitmap.compress(Bitmap.CompressFormat.JPEG, 40, bytes);

    File ExternalStorageDirectory = Environment.getExternalStorageDirectory();
    File file = new File(ExternalStorageDirectory + File.separator + fileName);
    
    FileOutputStream fileOutputStream = null;
    try {
     file.createNewFile();
     fileOutputStream = new FileOutputStream(file);
     fileOutputStream.write(bytes.toByteArray());
     
     ContentResolver cr = getContentResolver();
     String imagePath = file.getAbsolutePath();
     String name = file.getName();
     String description = "My bitmap created by Android-er";
     String savedURL = MediaStore.Images.Media
       .insertImage(cr, imagePath, name, description);
     
     Toast.makeText(MainActivity.this, 
       savedURL, 
       Toast.LENGTH_LONG).show();
     
    } catch (IOException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    } finally {
     if(fileOutputStream != null){
      try {
       fileOutputStream.close();
      } catch (IOException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
      }
     }
    }

   }});
    }

}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidsavebitmap.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_launcher" />

    <Button
        android:id="@+id/saveExternalStorageDirectory"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Save to ExternalStorageDirectory" />

    <Button
        android:id="@+id/saveMediaStore"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Save to MediaStore" />

    <Button
        android:id="@+id/saveExternalStorageDirectoryMediaStore"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Save to ExternalStorageDirectory and insert to MediaStore" />

</LinearLayout>


download filesDownload the files.

more:
Insert the bitmap to MediaStore with title and List images title in MediaStore

Thursday, April 16, 2015

Google Handwriting Input :)


Google Handwriting Input allows you to handwrite text on your phone or tablet in 82 languages. It supports printed and cursive writing, with or without a stylus. Google Handwriting Input also supports hundreds of emojis, so you can express yourself in any Android app.
Key features:
• A useful complement to touchscreen typing or voice input
• A fun way to enter emojis by drawing
• Useful for languages that can be challenging to type on a standard keyboard
• Works across your Android phones and tablets running Android 4.0.3 and up
• If you claim your handwriting is terrible, try it out and see if it can convince you otherwise

Wednesday, April 15, 2015

Bi-directional communication between Android and Arduino in USB Host Mode, example 3 - work with 8x8 LED Matrix

It's the 3rd example to implement Bi-directional communication between Android and Arduino in USB Host Mode, modified from another example "Android + Arduino Uno: display bits and ASCII char on LED Matrix".


On the Android to Arduino Uno direction, user tap on the custom view (8x8 squares) to turn ON/OFF LEDs on Arduino, or select character from the spiner.
On the Arduino Uno to Android direction, tap on the "Load from Arduino" button on Android, Android send a command to Arduino, then Arduino send back 64 bytes back to Android, represent (inverted) ON/OFF state of each dots.
The right hand side TextView show the data received on Android.


Roughly, to implement receiving on Android
- MainActivity implement Runnable
- override run() method to monitor UsbRequest
- start background thread after openDevice() to run the run() method

Connection:
Android - USB OTG Cable - USB Cable - Arduino Uno - 8x8 LED Matrix


Android side:

/res/layout/activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidtouchview.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/textstatus"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Status"
        android:textStyle="bold" />

    <Spinner
        android:id="@+id/asciispinner"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/load"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Load from Arduino" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="horizontal" >

        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:orientation="vertical" >

            <GridLayout
                android:id="@+id/mygrid"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:background="@android:color/background_light"
                android:columnCount="8"
                android:orientation="horizontal"
                android:rowCount="8" >
            </GridLayout>
        </LinearLayout>

        <TextView
            android:id="@+id/logtext"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:textColor="#FFFFFF"
            android:background="#A0A0A0" />
    </LinearLayout>

</LinearLayout>

MainActivity.java
package com.example.androidusbmatrix;

import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;

import com.example.androidusbmatrix.MyView.OnToggledListener;

import android.support.v7.app.ActionBarActivity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbRequest;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.GridLayout;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity 
 implements OnToggledListener, Runnable{

 Button btnLoad;
 TextView textStatus, logText;
 volatile MyView[] myViews;
 GridLayout myGridLayout;
 
 private static final int targetVendorID = 9025; //Arduino Uno
 private static final int targetProductID = 67; //Arduino Uno, not 0067
 private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
 PendingIntent mPermissionIntent;
 UsbDevice deviceFound = null;
 UsbInterface usbInterfaceFound = null;
 UsbInterface usbInterface;
 UsbDeviceConnection usbDeviceConnection;
 UsbEndpoint endpointIn = null;
 UsbEndpoint endpointOut = null;
 
 Spinner spAscii;
 AsciiCode Asciis[];
 ArrayAdapter<AsciiCode> asciiArrayAdapter;
 
 final byte SYNC_WORD   = (byte) 0xff;
 final byte CMD_01_Bits    = 0x01;
 final byte CMD_02_SingleChar  = 0x02;
 final byte CMD_03_Load   = 0x03;
 
 String logString = "";

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

  textStatus = (TextView) findViewById(R.id.textstatus);
  logText = (TextView) findViewById(R.id.logtext);
  myGridLayout = (GridLayout) findViewById(R.id.mygrid);

  int numOfCol = myGridLayout.getColumnCount();
  int numOfRow = myGridLayout.getRowCount();
  myViews = new MyView[numOfCol * numOfRow];
  for (int yPos = 0; yPos < numOfRow; yPos++) {
   for (int xPos = 0; xPos < numOfCol; xPos++) {
    MyView tView = new MyView(this, xPos, yPos);
    tView.setOnToggledListener(this);
    myViews[yPos * numOfCol + xPos] = tView;
    myGridLayout.addView(tView);
   }
  }

  myGridLayout.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {

     @Override
     public void onGlobalLayout() {

      int pLength;
      final int MARGIN = 5;

      int pWidth = myGridLayout.getWidth();
      int pHeight = myGridLayout.getHeight();
      int numOfCol = myGridLayout.getColumnCount();
      int numOfRow = myGridLayout.getRowCount();

      // Set myGridLayout equal width and height
      if (pWidth >= pHeight) {
       pLength = pHeight;
      } else {
       pLength = pWidth;
      }
      ViewGroup.LayoutParams pParams = myGridLayout
        .getLayoutParams();
      pParams.width = pLength;
      pParams.height = pLength;
      myGridLayout.setLayoutParams(pParams);

      int w = pLength / numOfCol; // pWidth/numOfCol;
      int h = pLength / numOfRow; // pHeight/numOfRow;

      for (int yPos = 0; yPos < numOfRow; yPos++) {
       for (int xPos = 0; xPos < numOfCol; xPos++) {
        GridLayout.LayoutParams params = (GridLayout.LayoutParams) myViews[yPos
          * numOfCol + xPos].getLayoutParams();
        params.width = w - 2 * MARGIN;
        params.height = h - 2 * MARGIN;
        params.setMargins(MARGIN, MARGIN, MARGIN,
          MARGIN);
        myViews[yPos * numOfCol + xPos]
          .setLayoutParams(params);
       }
      }

      // deprecated in API level 16
      myGridLayout.getViewTreeObserver()
        .removeGlobalOnLayoutListener(this);
      // for API Level >= 16
      // myGridLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
     }
    });
  
  // register the broadcast receiver
  mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(
   ACTION_USB_PERMISSION), 0);
  IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
  registerReceiver(mUsbReceiver, filter);

  registerReceiver(mUsbDeviceReceiver, new IntentFilter(
   UsbManager.ACTION_USB_DEVICE_ATTACHED));
  registerReceiver(mUsbDeviceReceiver, new IntentFilter(
   UsbManager.ACTION_USB_DEVICE_DETACHED));

  connectUsb();
  
  spAscii = (Spinner)findViewById(R.id.asciispinner);
  initAsciis();
  asciiArrayAdapter = new ArrayAdapter<AsciiCode>(this,
    android.R.layout.simple_list_item_1, 
    android.R.id.text1, 
    Asciis);
  asciiArrayAdapter.setDropDownViewResource(
    android.R.layout.simple_spinner_dropdown_item);
  spAscii.setAdapter(asciiArrayAdapter);
  spAscii.setOnItemSelectedListener(new OnItemSelectedListener(){

   @Override
   public void onItemSelected(AdapterView<?> parent, View view,
     int position, long id) {
    AsciiCode item = (AsciiCode) parent.getItemAtPosition(position);
    
    if(deviceFound != null){
     byte[] bytesSend = new byte[3];
     
     bytesSend[0] = SYNC_WORD;
     bytesSend[1] = CMD_02_SingleChar; //CMD
     bytesSend[2] = (byte) item.charAscii;
     
     usbDeviceConnection.bulkTransfer(endpointOut,
       bytesSend, bytesSend.length, 0);
    }
   }

   @Override
   public void onNothingSelected(AdapterView<?> arg0) {
    // TODO Auto-generated method stub
    
   }});
  
  btnLoad = (Button)findViewById(R.id.load);
  btnLoad.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    logString = "";
    logText.setText(logString);
    LoadFromArduino();
   }});
 }
 
 private void LoadFromArduino(){
  if(deviceFound != null){
   indexRx = 0;
   
   byte[] bytesSend = new byte[2];
   
   bytesSend[0] = SYNC_WORD;
   bytesSend[1] = CMD_03_Load;
   usbDeviceConnection.bulkTransfer(endpointOut,
     bytesSend, bytesSend.length, 0);

  }
 }

 @Override
 public void OnToggled(MyView v, boolean touchOn) {
  if(deviceFound != null){
   byte[] bytesSend = setupCmd();

   usbDeviceConnection.bulkTransfer(endpointOut,
     bytesSend, bytesSend.length, 0);
  }
 }
 
 private byte[] setupCmd(){
  byte[] bytes = new byte[18];
  
  bytes[0] = SYNC_WORD;
  bytes[1] = CMD_01_Bits; //CMD
  
  int i = 2;
  int numOfRow = 8;
  int numOfCol = 8;
  int numOfHalfCol = 4;

  for (int yPos = 0; yPos < numOfRow; yPos++) {
   byte b = 0x00;
   byte mask = 0x01;

   for (int xPos = 0; xPos < numOfHalfCol; xPos++) {
    
    if(myViews[yPos * numOfCol + xPos].touchOn){
     b = (byte) (b | mask);
    }
    
    mask = (byte) (mask << 1);
   }
   bytes[i] = b;
   
   i++;
   b = 0x00;
   mask = 0x10;
   for (int xPos = numOfHalfCol; xPos < numOfCol; xPos++) {
    
    if(myViews[yPos * numOfCol + xPos].touchOn){
     b = (byte) (b | mask);
    }
    
    mask = (byte) (mask << 1);
   }
   bytes[i] = b;
   
   i++;
  }

  return bytes;
 }
 
 //usb related
 private boolean setupUsbComm() {

  // for more info, search SET_LINE_CODING and
  // SET_CONTROL_LINE_STATE in the document:
  // "Universal Serial Bus Class Definitions for Communication Devices"
  // at http://adf.ly/dppFt
  final int RQSID_SET_LINE_CODING = 0x20;
  final int RQSID_SET_CONTROL_LINE_STATE = 0x22;

  boolean success = false;

  UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
  Boolean permitToRead = manager.hasPermission(deviceFound);

  if (permitToRead) {
   usbDeviceConnection = manager.openDevice(deviceFound);
   if (usbDeviceConnection != null) {
    usbDeviceConnection.claimInterface(usbInterfaceFound, true);

    usbDeviceConnection.controlTransfer(0x21, // requestType
      RQSID_SET_CONTROL_LINE_STATE, // SET_CONTROL_LINE_STATE
      0, // value
      0, // index
      null, // buffer
      0, // length
      0); // timeout

    // baud rate = 9600
    // 8 data bit
    // 1 stop bit
    byte[] encodingSetting = new byte[] { (byte) 0x80, 0x25, 0x00,
      0x00, 0x00, 0x00, 0x08 };
    usbDeviceConnection.controlTransfer(0x21, // requestType
      RQSID_SET_LINE_CODING, // SET_LINE_CODING
      0, // value
      0, // index
      encodingSetting, // buffer
      7, // length
      0); // timeout

    //2015-04-15
                Thread thread = new Thread(this);
                thread.start();
   }

  } else {
   manager.requestPermission(deviceFound, mPermissionIntent);
   textStatus.setText("Permission: " + permitToRead);
  }

  return success;
 }
 
 private void connectUsb() {

  textStatus.setText("connectUsb()");

  searchEndPoint();

  if (usbInterfaceFound != null) {
   setupUsbComm();
  }

 }
 
 private void releaseUsb() {

  textStatus.setText("releaseUsb()");

  if (usbDeviceConnection != null) {
   if (usbInterface != null) {
    usbDeviceConnection.releaseInterface(usbInterface);
    usbInterface = null;
   }
   usbDeviceConnection.close();
   usbDeviceConnection = null;
  }

  deviceFound = null;
  usbInterfaceFound = null;
  endpointIn = null;
  endpointOut = null;
 }

 private void searchEndPoint() {

  usbInterfaceFound = null;
  endpointOut = null;
  endpointIn = null;

  // Search device for targetVendorID and targetProductID
  if (deviceFound == null) {
   UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
   HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
   Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();

   while (deviceIterator.hasNext()) {
    UsbDevice device = deviceIterator.next();

    if (device.getVendorId() == targetVendorID) {
     if (device.getProductId() == targetProductID) {
      deviceFound = device;
     }
    }
   }
  }

  if (deviceFound == null) {
   textStatus.setText("device not found");
  } else {

   // Search for UsbInterface with Endpoint of USB_ENDPOINT_XFER_BULK,
   // and direction USB_DIR_OUT and USB_DIR_IN

   for (int i = 0; i < deviceFound.getInterfaceCount(); i++) {
    UsbInterface usbif = deviceFound.getInterface(i);

    UsbEndpoint tOut = null;
    UsbEndpoint tIn = null;

    int tEndpointCnt = usbif.getEndpointCount();
    if (tEndpointCnt >= 2) {
     for (int j = 0; j < tEndpointCnt; j++) {
      if (usbif.getEndpoint(j).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
       if (usbif.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_OUT) {
        tOut = usbif.getEndpoint(j);
       } else if (usbif.getEndpoint(j).getDirection() == UsbConstants.USB_DIR_IN) {
        tIn = usbif.getEndpoint(j);
       }
      }
     }

     if (tOut != null && tIn != null) {
      // This interface have both USB_DIR_OUT
      // and USB_DIR_IN of USB_ENDPOINT_XFER_BULK
      usbInterfaceFound = usbif;
      endpointOut = tOut;
      endpointIn = tIn;
     }
    }

   }

  }
 }
 
 private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {

  @Override
  public void onReceive(Context context, Intent intent) {
   String action = intent.getAction();
   if (ACTION_USB_PERMISSION.equals(action)) {

    textStatus.setText("ACTION_USB_PERMISSION");

    synchronized (this) {
     UsbDevice device = (UsbDevice) intent
       .getParcelableExtra(UsbManager.EXTRA_DEVICE);

     if (intent.getBooleanExtra(
       UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
      if (device != null) {
       connectUsb();
      }
     } else {
      textStatus.setText("permission denied for device ");
     }
    }
   }
  }
 };

 private final BroadcastReceiver mUsbDeviceReceiver = new BroadcastReceiver() {

  @Override
  public void onReceive(Context context, Intent intent) {
   String action = intent.getAction();
   if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {

    deviceFound = (UsbDevice) intent
      .getParcelableExtra(UsbManager.EXTRA_DEVICE);

    textStatus.setText("ACTION_USB_DEVICE_ATTACHED");

    connectUsb();

   } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {

    UsbDevice device = (UsbDevice) intent
      .getParcelableExtra(UsbManager.EXTRA_DEVICE);

    textStatus.setText("ACTION_USB_DEVICE_DETACHED");

    if (device != null) {
     if (device == deviceFound) {
      releaseUsb();
     }
    }
    
   }
  }

 };
 
 public void initAsciis() {
  Asciis = new AsciiCode[128];
  for (int i = 0; i < 128; i++) {
   Asciis[i] = new AsciiCode(i);
  }
 }
 
 class AsciiCode{
  int id;
  char charAscii;
  
  AsciiCode(int id){
   this.id = id;
   charAscii = (char)id;
  }
  
  public String toString(){
   return 
    String.format("%03d", id)
    + " /(hex) 0x" + String.format("%02x", id).toUpperCase()
    + " : " + charAscii;
  }

 }

 int indexRx;
 final int RX_STATE_0_IDLE = 0;
 final int RX_STATE_1_RXing = 1;
 int RxState;
 final byte PIXELON = 'A';
 final byte PIXELOFF = 'B';
 //Handle receiving from Arduino
 @Override
 public void run() {
  RxState = RX_STATE_0_IDLE;
  ByteBuffer buffer = ByteBuffer.allocate(1);
        UsbRequest request = new UsbRequest();
        request.initialize(usbDeviceConnection, endpointIn);
        while (true) {
         request.queue(buffer, 1);
         if (usbDeviceConnection.requestWait() == request) {
          final byte dataRx = buffer.get(0);
          
          
          logString += String.format("%02d", dataRx) + " ";
          runOnUiThread(new Runnable(){

           @Override
           public void run() {
            
            logText.setText(logString);
           }});

          if(indexRx < myViews.length){
           //only accept two value
           //ignore unaccept value
           //I found always have '0' received, 
           //I don't know why!
              if(dataRx == PIXELON){
               myViews[indexRx].touchOn = true;
               rqsRedrawMyView(indexRx);
               indexRx++;
              }else if(dataRx == PIXELOFF){
               myViews[indexRx].touchOn = false;
               rqsRedrawMyView(indexRx);
               indexRx++;
              }
              
          }
         }else{
          break;
         }
         
        }
 }
 
 private void rqsRedrawMyView(final int i){
  runOnUiThread(new Runnable(){

   @Override
   public void run() {
    myViews[i].invalidate();
   }});
 }

}

MyView.java, custom view for each square
package com.example.androidusbmatrix;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

public class MyView extends View {
 
 public interface OnToggledListener {
  void OnToggled(MyView v, boolean touchOn);
 }

 boolean touchOn;
 boolean mDownTouch = false;
 private OnToggledListener toggledListener;
 int idX = 0; //default
 int idY = 0; //default

 public MyView(Context context, int x, int y) {
  super(context);
  idX = x;
  idY = y;
  init();
 }
 
 public MyView(Context context) {
  super(context);
  init();
 }

 public MyView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }

 public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }

 private void init() {
  touchOn = false;
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
    MeasureSpec.getSize(heightMeasureSpec));
 }

 @Override
 protected void onDraw(Canvas canvas) {
  if (touchOn) {
   canvas.drawColor(Color.RED);
  } else {
   canvas.drawColor(Color.GRAY);
  }
 }

 @Override
 public boolean onTouchEvent(MotionEvent event) {
  super.onTouchEvent(event);

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
             
             touchOn = !touchOn;
       invalidate();
       
       if(toggledListener != null){
        toggledListener.OnToggled(this, touchOn);
       }
             
                mDownTouch = true;
                return true;

            case MotionEvent.ACTION_UP:
                if (mDownTouch) {
                    mDownTouch = false;
                    performClick();
                    return true;
                }
        }
        return false;
 }

 @Override
 public boolean performClick() {
        super.performClick();
        return true;
 }
 
 public void setOnToggledListener(OnToggledListener listener){
  toggledListener = listener;
 }
 
 public int getIdX(){
  return idX;
 }
 
 public int getIdY(){
  return idY;
 }

}

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androidusbmatrix"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-feature android:name="android.hardware.usb.host" />
    <uses-sdk
        android:minSdkVersion="14"
        android:targetSdkVersion="21" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
            </intent-filter>
            <meta-data
                android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
                android:resource="@xml/device_filter" />
            <intent-filter>
                <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
            </intent-filter>
        </activity>
    </application>

</manifest>

/res/xml/device_filter.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- idVendor=2341, idProduct=0043 for Arduino Uno R3 -->
    <usb-device 
        vendor-id="9025" 
        product-id="0067" />
</resources>


download filesDownload the files.

Arduino side:

UnoUsbMatrix.ino
/*
 *  http://android-er.blogspot.com
 *  http://arduino-er.blogspot.com/
 */

byte font[128][8] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0000 (nul)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0001
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0002
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0003
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0004
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0005
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0006
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0007
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0008
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0009
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000A
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000B
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000C
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000D
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000E
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+000F
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0010
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0011
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0012
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0013
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0014
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0015
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0016
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0017
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0018
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0019
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001A
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001B
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001C
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001D
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001E
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+001F
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0020 (space)
    { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00},   // U+0021 (!)
    { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0022 (")
    { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00},   // U+0023 (#)
    { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00},   // U+0024 ($)
    { 0x00, 0x63, 0x33, 0x18, 0x0C, 0x66, 0x63, 0x00},   // U+0025 (%)
    { 0x1C, 0x36, 0x1C, 0x6E, 0x3B, 0x33, 0x6E, 0x00},   // U+0026 (&)
    { 0x06, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0027 (')
    { 0x18, 0x0C, 0x06, 0x06, 0x06, 0x0C, 0x18, 0x00},   // U+0028 (()
    { 0x06, 0x0C, 0x18, 0x18, 0x18, 0x0C, 0x06, 0x00},   // U+0029 ())
    { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00},   // U+002A (*)
    { 0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00},   // U+002B (+)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+002C (,)
    { 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00},   // U+002D (-)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+002E (.)
    { 0x60, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x01, 0x00},   // U+002F (/)
    { 0x3E, 0x63, 0x73, 0x7B, 0x6F, 0x67, 0x3E, 0x00},   // U+0030 (0)
    { 0x0C, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00},   // U+0031 (1)
    { 0x1E, 0x33, 0x30, 0x1C, 0x06, 0x33, 0x3F, 0x00},   // U+0032 (2)
    { 0x1E, 0x33, 0x30, 0x1C, 0x30, 0x33, 0x1E, 0x00},   // U+0033 (3)
    { 0x38, 0x3C, 0x36, 0x33, 0x7F, 0x30, 0x78, 0x00},   // U+0034 (4)
    { 0x3F, 0x03, 0x1F, 0x30, 0x30, 0x33, 0x1E, 0x00},   // U+0035 (5)
    { 0x1C, 0x06, 0x03, 0x1F, 0x33, 0x33, 0x1E, 0x00},   // U+0036 (6)
    { 0x3F, 0x33, 0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x00},   // U+0037 (7)
    { 0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00},   // U+0038 (8)
    { 0x1E, 0x33, 0x33, 0x3E, 0x30, 0x18, 0x0E, 0x00},   // U+0039 (9)
    { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x00},   // U+003A (:)
    { 0x00, 0x0C, 0x0C, 0x00, 0x00, 0x0C, 0x0C, 0x06},   // U+003B (//)
    { 0x18, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x18, 0x00},   // U+003C (<)
    { 0x00, 0x00, 0x3F, 0x00, 0x00, 0x3F, 0x00, 0x00},   // U+003D (=)
    { 0x06, 0x0C, 0x18, 0x30, 0x18, 0x0C, 0x06, 0x00},   // U+003E (>)
    { 0x1E, 0x33, 0x30, 0x18, 0x0C, 0x00, 0x0C, 0x00},   // U+003F (?)
    { 0x3E, 0x63, 0x7B, 0x7B, 0x7B, 0x03, 0x1E, 0x00},   // U+0040 (@)
    { 0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00},   // U+0041 (A)
    { 0x3F, 0x66, 0x66, 0x3E, 0x66, 0x66, 0x3F, 0x00},   // U+0042 (B)
    { 0x3C, 0x66, 0x03, 0x03, 0x03, 0x66, 0x3C, 0x00},   // U+0043 (C)
    { 0x1F, 0x36, 0x66, 0x66, 0x66, 0x36, 0x1F, 0x00},   // U+0044 (D)
    { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x46, 0x7F, 0x00},   // U+0045 (E)
    { 0x7F, 0x46, 0x16, 0x1E, 0x16, 0x06, 0x0F, 0x00},   // U+0046 (F)
    { 0x3C, 0x66, 0x03, 0x03, 0x73, 0x66, 0x7C, 0x00},   // U+0047 (G)
    { 0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00},   // U+0048 (H)
    { 0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0049 (I)
    { 0x78, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E, 0x00},   // U+004A (J)
    { 0x67, 0x66, 0x36, 0x1E, 0x36, 0x66, 0x67, 0x00},   // U+004B (K)
    { 0x0F, 0x06, 0x06, 0x06, 0x46, 0x66, 0x7F, 0x00},   // U+004C (L)
    { 0x63, 0x77, 0x7F, 0x7F, 0x6B, 0x63, 0x63, 0x00},   // U+004D (M)
    { 0x63, 0x67, 0x6F, 0x7B, 0x73, 0x63, 0x63, 0x00},   // U+004E (N)
    { 0x1C, 0x36, 0x63, 0x63, 0x63, 0x36, 0x1C, 0x00},   // U+004F (O)
    { 0x3F, 0x66, 0x66, 0x3E, 0x06, 0x06, 0x0F, 0x00},   // U+0050 (P)
    { 0x1E, 0x33, 0x33, 0x33, 0x3B, 0x1E, 0x38, 0x00},   // U+0051 (Q)
    { 0x3F, 0x66, 0x66, 0x3E, 0x36, 0x66, 0x67, 0x00},   // U+0052 (R)
    { 0x1E, 0x33, 0x07, 0x0E, 0x38, 0x33, 0x1E, 0x00},   // U+0053 (S)
    { 0x3F, 0x2D, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0054 (T)
    { 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x3F, 0x00},   // U+0055 (U)
    { 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0056 (V)
    { 0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00},   // U+0057 (W)
    { 0x63, 0x63, 0x36, 0x1C, 0x1C, 0x36, 0x63, 0x00},   // U+0058 (X)
    { 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x1E, 0x00},   // U+0059 (Y)
    { 0x7F, 0x63, 0x31, 0x18, 0x4C, 0x66, 0x7F, 0x00},   // U+005A (Z)
    { 0x1E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x1E, 0x00},   // U+005B ([)
    { 0x03, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x40, 0x00},   // U+005C (\)
    { 0x1E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x1E, 0x00},   // U+005D (])
    { 0x08, 0x1C, 0x36, 0x63, 0x00, 0x00, 0x00, 0x00},   // U+005E (^)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},   // U+005F (_)
    { 0x0C, 0x0C, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+0060 (`)
    { 0x00, 0x00, 0x1E, 0x30, 0x3E, 0x33, 0x6E, 0x00},   // U+0061 (a)
    { 0x07, 0x06, 0x06, 0x3E, 0x66, 0x66, 0x3B, 0x00},   // U+0062 (b)
    { 0x00, 0x00, 0x1E, 0x33, 0x03, 0x33, 0x1E, 0x00},   // U+0063 (c)
    { 0x38, 0x30, 0x30, 0x3e, 0x33, 0x33, 0x6E, 0x00},   // U+0064 (d)
    { 0x00, 0x00, 0x1E, 0x33, 0x3f, 0x03, 0x1E, 0x00},   // U+0065 (e)
    { 0x1C, 0x36, 0x06, 0x0f, 0x06, 0x06, 0x0F, 0x00},   // U+0066 (f)
    { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0067 (g)
    { 0x07, 0x06, 0x36, 0x6E, 0x66, 0x66, 0x67, 0x00},   // U+0068 (h)
    { 0x0C, 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+0069 (i)
    { 0x30, 0x00, 0x30, 0x30, 0x30, 0x33, 0x33, 0x1E},   // U+006A (j)
    { 0x07, 0x06, 0x66, 0x36, 0x1E, 0x36, 0x67, 0x00},   // U+006B (k)
    { 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x00},   // U+006C (l)
    { 0x00, 0x00, 0x33, 0x7F, 0x7F, 0x6B, 0x63, 0x00},   // U+006D (m)
    { 0x00, 0x00, 0x1F, 0x33, 0x33, 0x33, 0x33, 0x00},   // U+006E (n)
    { 0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00},   // U+006F (o)
    { 0x00, 0x00, 0x3B, 0x66, 0x66, 0x3E, 0x06, 0x0F},   // U+0070 (p)
    { 0x00, 0x00, 0x6E, 0x33, 0x33, 0x3E, 0x30, 0x78},   // U+0071 (q)
    { 0x00, 0x00, 0x3B, 0x6E, 0x66, 0x06, 0x0F, 0x00},   // U+0072 (r)
    { 0x00, 0x00, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x00},   // U+0073 (s)
    { 0x08, 0x0C, 0x3E, 0x0C, 0x0C, 0x2C, 0x18, 0x00},   // U+0074 (t)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x6E, 0x00},   // U+0075 (u)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00},   // U+0076 (v)
    { 0x00, 0x00, 0x63, 0x6B, 0x7F, 0x7F, 0x36, 0x00},   // U+0077 (w)
    { 0x00, 0x00, 0x63, 0x36, 0x1C, 0x36, 0x63, 0x00},   // U+0078 (x)
    { 0x00, 0x00, 0x33, 0x33, 0x33, 0x3E, 0x30, 0x1F},   // U+0079 (y)
    { 0x00, 0x00, 0x3F, 0x19, 0x0C, 0x26, 0x3F, 0x00},   // U+007A (z)
    { 0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00},   // U+007B ({)
    { 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x18, 0x00},   // U+007C (|)
    { 0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00},   // U+007D (})
    { 0x6E, 0x3B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   // U+007E (~)
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}    // U+007F
};

// 2-dimensional array of row pin numbers:
const int row[8] = {
  2, 7, 19, 5, 13, 18, 12, 16
};

// 2-dimensional array of column pin numbers:
const int col[8] = {
  6, 11, 10, 3, 17, 4, 8, 9
};

// 2-dimensional array of pixels:
int pixels[8][8];

const int ST_0 = 0;       //waiting Sync word
const int ST_1_CMD = 1;   //Waiting CMD
const int ST_2_DATA= 2;  //Receiving Data for CMD_01_Bits
const int ST_3_CHAR= 3;  //Receiving Single Char for CMD_02_SingleChar
const int DATA_LENGTH = 16;  //length of data = 16
const byte SYNC_WORD = 0xFF;
const byte CMD_01_Bits = 0x01;        //Send as 8x8 bit map
const byte CMD_02_SingleChar = 0x02;  //single char
const byte CMD_03_Load = 0x03;        //Load from Arduino to Android
int cmdState;
int dataIndex;

byte data[DATA_LENGTH];

//represent pixel ON/OFF send back to Android
const byte PIXELON = 'A';
const byte PIXELOFF = 'B';

void setup() {
  
  Serial.begin(9600);

  // initialize the I/O pins as outputs
  // iterate over the pins:
  for (int thisPin = 0; thisPin < 8; thisPin++) {
    // initialize the output pins:
    pinMode(col[thisPin], OUTPUT);
    pinMode(row[thisPin], OUTPUT);
    // take the col pins (i.e. the cathodes) high to ensure that
    // the LEDS are off:
    digitalWrite(col[thisPin], HIGH);
  }
  
  for(int y=0; y<8; y++){
    for(int x=0; x<8; x++){
      pixels[x][y] = HIGH;
    }
  }
  
  cmdState = ST_0;
}

void loop() {
  refreshScreen();
  
  if(Serial.available()){
    cmdHandle(Serial.read());
  }

}

void cmdHandle(int incomingByte){
  
  //prevent from lost-sync
  if(incomingByte == SYNC_WORD){
    cmdState = ST_1_CMD;
    return;
  }
  
  switch(cmdState){
    case ST_0:
        if(incomingByte == SYNC_WORD){
          cmdState = ST_1_CMD;
        }
        break;
    case ST_1_CMD:{
          switch(incomingByte){
            case CMD_01_Bits:
                cmdState = ST_2_DATA;
                dataIndex = 0;
                break;
            case CMD_02_SingleChar:
                cmdState = ST_3_CHAR;
                break;
            case CMD_03_Load:
                //The program will block here
                //also no LED scan
                SendToAndroid();  
                cmdState = ST_0;
                break;
            default:
                cmdState = ST_0;
          }
        }
        break;
    case ST_2_DATA:{
          data[dataIndex] = incomingByte;
          dataIndex++;
        
          int iData = 0;
          if(dataIndex==DATA_LENGTH){
            cmdState = ST_0;
            for(int i=0; i<8; i++){
              byte mask = 0x01;
            
              for(int j=0; j<4; j++){
                  if (data[iData] & mask){
                    pixels[i][j] = LOW;
                  }else{
                    pixels[i][j] = HIGH;
                  }
                  mask = mask << 1;
              }
            
              iData++;
              for(int j=4; j<8; j++){
                  if (data[iData] & mask){
                    pixels[i][j] = LOW;
                  }else{
                    pixels[i][j] = HIGH;
                  }
                  mask = mask << 1;
              }
            
              iData++;
            }
          }
        }
        break;
      
      case ST_3_CHAR:
        setupChar((char)incomingByte);
        cmdState = ST_0;
        break;
        
  }
  
}

void SendToAndroid(){
  for(int i=0; i<8; i++){
    for(int j=0; j<8; j++){
      //send back inverted ON/OFF state
      if(pixels[i][j] == LOW){
        Serial.write(PIXELOFF);
      }else{
        Serial.write(PIXELON);
      }
      delay(10);
    }
  }
}

void setupChar(char c){

  for (int x = 0; x < 8; x++) {
    byte bitMask = 0x01;
    byte f = font[c][x];
    for (int y = 0; y < 8; y++) {
      if (f & bitMask){
        pixels[x][y] = LOW;
      }else{
        pixels[x][y] = HIGH;
      }
      bitMask = bitMask << 1;
    }
  }

}

void refreshScreen() {
  // iterate over the rows (anodes):
  for (int thisRow = 0; thisRow < 8; thisRow++) {
    // take the row pin (anode) high:
    digitalWrite(row[thisRow], HIGH);
    // iterate over the cols (cathodes):
    for (int thisCol = 0; thisCol < 8; thisCol++) {
      // get the state of the current pixel;
      int thisPixel = pixels[thisRow][thisCol];
      // when the row is HIGH and the col is LOW,
      // the LED where they meet turns on:
      digitalWrite(col[thisCol], thisPixel);
      // turn the pixel off:
      if (thisPixel == LOW) {
        digitalWrite(col[thisCol], HIGH);
      }
    }
    // take the row pin low to turn off the whole row:
    digitalWrite(row[thisRow], LOW);
  }
}

Download Arduino ino code HERE.

Connection between Arduino Uno and 8x8 LED Matrix:


Check on the right hand side TextView, the received data log: There are some unexpected '00' received. I don't know the reason! Please anybody can advise.


Related:
- More example of communication between Android to Arduino Uno, in USB Host Mode

Tuesday, April 14, 2015

Auto dismiss popupWindow with AsyncTask

This example we will open PopupWindow together with a AsyncTask. Once the AsyncTask count for 10 seconds, it will dismiss the PopupWindow; to show how to dismiss PopupWindow outside itself.




/res/layout/popup.xml, the layout of the PopupWindow.
package com.example.androidpopupwindow;

import android.support.v7.app.ActionBarActivity;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;

public class MainActivity extends ActionBarActivity {

 LinearLayout mainLayout;
 ProgressBar progressBar;
 TextView msg;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  mainLayout = (LinearLayout)findViewById(R.id.mainlayout);

  progressBar = (ProgressBar) findViewById(R.id.progressbar);
  msg = (TextView)findViewById(R.id.msg);
  
  final Button btnOpenPopup = (Button) findViewById(R.id.openpopup);
  btnOpenPopup.setOnClickListener(new Button.OnClickListener() {

   @Override
   public void onClick(View arg0) {
    LayoutInflater layoutInflater = (LayoutInflater) getBaseContext()
      .getSystemService(LAYOUT_INFLATER_SERVICE);
    View popupView = layoutInflater.inflate(R.layout.popup, null);
    final PopupWindow popupWindow = new PopupWindow(popupView,
      LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

    Button btnDismiss = (Button) popupView
      .findViewById(R.id.dismiss);
    btnDismiss.setOnClickListener(new Button.OnClickListener() {

     @Override
     public void onClick(View v) {
      // TODO Auto-generated method stub
      popupWindow.dismiss();
     }
    });
    
    TextView me = (TextView) popupView
      .findViewById(R.id.me);
    me.setText(popupWindow.toString());

    //Center the PopupWindow
    popupWindow.showAtLocation(mainLayout, Gravity.CENTER, 0, 0);

    //Auto dismiss PopupWindow in 10 sec
    new DismissAsyncTask(progressBar, msg, popupWindow).execute();
   }
  });
 }

 public class DismissAsyncTask extends AsyncTask<Void, Integer, Void> {

  ProgressBar taskProgressBar;
  TextView taskMsg;
  PopupWindow taskPopupWindow;

  public DismissAsyncTask(ProgressBar targetProgressBar,
    TextView targetMsg,
    PopupWindow targetPopupWindow) {
   taskProgressBar = targetProgressBar;
   taskMsg = targetMsg;
   taskProgressBar.setVisibility(View.VISIBLE);
   taskPopupWindow = targetPopupWindow;
  }

  @Override
  protected Void doInBackground(Void... params) {
   for (int i = 0; i < 10; i++) {
    publishProgress(i);
    SystemClock.sleep(1000);
   }
   return null;
  }

  @Override
  protected void onProgressUpdate(Integer... values) {
   taskProgressBar.setVisibility(View.VISIBLE);
   taskProgressBar.setProgress(values[0]);
   
   taskMsg.setText("Updated by : " 
    + taskPopupWindow.toString() 
    + " - " + values[0]);
  }

  @Override
  protected void onPostExecute(Void result) {
   Toast.makeText(MainActivity.this, 
    "Dismiss PopupWindows: " + taskPopupWindow.toString(), 
    Toast.LENGTH_LONG).show();
   
   taskProgressBar.setVisibility(View.INVISIBLE);
   taskPopupWindow.dismiss();
  }
 }

}

/res/layout/activity_main.xml, the layout of the main activity.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/mainlayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidpopupwindow.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />

    <Button
        android:id="@+id/openpopup"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Open Popup Window" />
    
    <ProgressBar
        android:id="@+id/progressbar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:max="10"
        android:progress="0"
        android:visibility="invisible" />
    <TextView
        android:id="@+id/msg"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

MainActivity.java
package com.example.androidpopupwindow;

import android.support.v7.app.ActionBarActivity;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;

public class MainActivity extends ActionBarActivity {

 LinearLayout mainLayout;
 ProgressBar progressBar;
 TextView msg;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  mainLayout = (LinearLayout)findViewById(R.id.mainlayout);

  progressBar = (ProgressBar) findViewById(R.id.progressbar);
  msg = (TextView)findViewById(R.id.msg);
  
  final Button btnOpenPopup = (Button) findViewById(R.id.openpopup);
  btnOpenPopup.setOnClickListener(new Button.OnClickListener() {

   @Override
   public void onClick(View arg0) {
    LayoutInflater layoutInflater = (LayoutInflater) getBaseContext()
      .getSystemService(LAYOUT_INFLATER_SERVICE);
    View popupView = layoutInflater.inflate(R.layout.popup, null);
    final PopupWindow popupWindow = new PopupWindow(popupView,
      LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

    Button btnDismiss = (Button) popupView
      .findViewById(R.id.dismiss);
    btnDismiss.setOnClickListener(new Button.OnClickListener() {

     @Override
     public void onClick(View v) {
      // TODO Auto-generated method stub
      popupWindow.dismiss();
     }
    });
    
    TextView me = (TextView) popupView
      .findViewById(R.id.me);
    me.setText(popupWindow.toString());

    //Center the PopupWindow
    popupWindow.showAtLocation(mainLayout, Gravity.CENTER, 0, 0);

    //Auto dismiss PopupWindow in 10 sec
    new DismissAsyncTask(progressBar, msg, popupWindow).execute();
   }
  });
 }

 public class DismissAsyncTask extends AsyncTask<Void, Integer, Void> {

  ProgressBar taskProgressBar;
  TextView taskMsg;
  PopupWindow taskPopupWindow;

  public DismissAsyncTask(ProgressBar targetProgressBar,
    TextView targetMsg,
    PopupWindow targetPopupWindow) {
   taskProgressBar = targetProgressBar;
   taskMsg = targetMsg;
   taskProgressBar.setVisibility(View.VISIBLE);
   taskPopupWindow = targetPopupWindow;
  }

  @Override
  protected Void doInBackground(Void... params) {
   for (int i = 0; i < 10; i++) {
    publishProgress(i);
    SystemClock.sleep(1000);
   }
   return null;
  }

  @Override
  protected void onProgressUpdate(Integer... values) {
   taskProgressBar.setVisibility(View.VISIBLE);
   taskProgressBar.setProgress(values[0]);
   
   taskMsg.setText("Updated by : " 
    + taskPopupWindow.toString() 
    + " - " + values[0]);
  }

  @Override
  protected void onPostExecute(Void result) {
   Toast.makeText(MainActivity.this, 
    "Dismiss PopupWindows: " + taskPopupWindow.toString(), 
    Toast.LENGTH_LONG).show();
   
   taskProgressBar.setVisibility(View.INVISIBLE);
   taskPopupWindow.dismiss();
  }
 }

}



Remark for AsyncTask:
- In this implementation, I haven't stop the AsyncTask if the PopupWindow dismissed by button. So the associated AsyncTask still run in background. I intentionally implement like this, to show the behavior of AsyncTask.
- Starting with HONEYCOMB, AsyncTask are executed on a single thread, that means one by one (read Run multi AsyncTask at the same time). If user start a PopupWindow A and AsyncTask A, and dismiss PopupWindow A with button, AsyncTask A still running. Then start PopupWindow B and AsyncTask B. It will wait AsyncTask A finished, then start AsyncTask B,...then finish, then dismiss PopupWindow B.

download filesDownload the files.

Near Field Communication with Android Cookbook

Discover the endless possibilities of using Android NFC capabilities to enhance your apps over 50 practical recipes

Near Field Communication with Android Cookbook

Overview
  • Practical and real-life examples showing how and where NFC can be used
  • Discover how to exploit NFC capabilities to enhance your apps to easily share and interact with the world
  • Learn how to extend cross-device content sharing by taking advantage of Android Beam's capabilities
In Detail

Near Field Communication, or simply NFC, is an emerging technology with endless applicability. Its low battery consumption and simplicity are the keys to its success.

Near Field Communication with Android Cookbook is a hands-on book that will help you to set up your development environment, get to know the basics of NFC, and then use what you learn to create more enhanced and practical applications. This practical guide will teach you all you need know about NFC to get you started in developing outstanding out-of-the-box applications.

What you will learn from this book
  • Work with the foreground dispatch system NFC data exchange format
  • Create several applications to consolidate knowledge and see what NFC can be used for
  • Read and write tags with URI, text, mime, and external types
  • Share content across two NFC-enabled devices
  • Extend NFC usage using Bluetooth and Wi-Fi
  • Combine NFC with social networks and games
  • Use Open NFC for Android to set up a virtual development and testing environment
Approach

An easy-to-follow guide, full of hands-on examples of and real-world applications. Each recipe is explained and placed in context.

Who this book is written for

If you want to learn how to create NFC-enabled Android applications, this is the book for you. Perhaps you already know a bit about Android application developments but have never used NFC, or perhaps you know a little about NFC android development but want some more advanced features and examples. In either case, this book will get you up and running quickly. You are expected to have Android programming knowledge.

Beginning Android Wearables

Although wearable devices have existed since the 70s, they have only recently become mainstream. Google Glass and Android Wear smartwatches, in particular, are relatively new devices that provide countless opportunities for creating innovative apps with unprecedented user experiences. Beginning Android Wearables gives you the skills you need to take part in the wearable revolution and to delight your users by providing the information they need at the tips of their fingers.

Beginning Android Wearables

This book is very practical and contains many examples that not only show you how to write code for Glass and Android Wear, but also demonstrate how to apply this code in the context of an app.

What you’ll learn
  • Build notifications for handheld apps that are automatically shared with Glass or Android Wear, and learn to customize these notifications specifically for wearables
  • Create apps that run directly on Android Wear devices and leverage the Android Wear SDK
  • Use the wearable UI library to build effective user interfaces for Android Wear
  • Use the wearable data layer API to share and synchronize data between a handheld device and Android Wear
  • Build custom watch faces for Android Wear
  • Build Glassware (that is, apps for Glass) using the Glass Development Kit (GDK)
  • Use the views and widgets of the GDK to build effective user interfaces for Glass
  • Receive user input on Glass with touchpad gestures, head gestures, and voice recognition
  • Take pictures and record videos on Glass by either leveraging the native camera app or with the Camera API
  • Utilize location providers and orientation sensors to build contextually sensitive apps
Who this book is for
This book only requires basic knowledge of Android programming. Prior to this book, you should have read or are at least comfortable with Android after using Apress tutorials, Android Apps for Absolute Beginners or Beginning Android

Table of Contents

Part I - Introduction
1. Introducing Android Wearables

Part II - Notifications
2. Reviewing Notifications for Android
3. Customizing Notifications for Wearables

Part III - Android Wear
4. Running Apps Directly on Android Wear
5. Android Wear UI Essentials
6. The Wearable Data Layer API
7. Creating Custom Watch Faces

Part IV - Google Glass
8. Running Apps Directly on Glass
9. Glass User Interface Essentials
10. Voice and Input
11. The Camera: Taking Pictures and Recording Video

Part V - Android Wear and Glass
12. Location and Orientation

Monday, April 13, 2015

Set icon and background of ActionBar

Android programming example to set icon and background of ActionBar. (Assume your activity extends android.support.v7.app.ActionBarActivity, with android.support.v7.app.ActionBar)




package com.example.androidactionbartitle;

import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.content.res.Resources;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;

public class MainActivity extends ActionBarActivity {
 
 Button btn1, btn2, btn3, btn4, btn5;;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        btn1 = (Button)findViewById(R.id.button1);
        btn2 = (Button)findViewById(R.id.button2);
        btn3 = (Button)findViewById(R.id.button3);
        btn4 = (Button)findViewById(R.id.button4);
        btn5 = (Button)findViewById(R.id.button5);

        btn1.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    ActionBar actionBar = getSupportActionBar();
    
    Drawable drawable;
    Resources resources = getResources();
    
    //deprecated in API level 22
    //drawable = resources.getDrawable(R.drawable.ic_launcher);
    
    //API Level 21
    drawable = resources.getDrawable(R.drawable.ic_launcher, null);
    
    actionBar.setBackgroundDrawable(drawable);
   }});
        
        btn2.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    ActionBar actionBar = getSupportActionBar();
    
    Drawable drawable;
    drawable = new ColorDrawable(0xA0500050);
    
    actionBar.setBackgroundDrawable(drawable);
   }});
        
        btn3.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    ActionBar actionBar = getSupportActionBar();
    actionBar.setBackgroundDrawable(null);
   }});
        
        btn4.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    ActionBar actionBar = getSupportActionBar();
    
    Drawable drawable;
    Resources resources = getResources();
    
    //deprecated in API level 22
    //drawable = resources.getDrawable(R.drawable.ic_launcher);
    
    //API Level 21
    drawable = resources.getDrawable(R.drawable.ic_launcher, null);
    
    actionBar.setIcon(drawable);
    actionBar.setDisplayShowHomeEnabled(true);
   }});
        
        btn5.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    //enable display HOME, but with icon of null
    ActionBar actionBar = getSupportActionBar();
    actionBar.setIcon(null);
    actionBar.setDisplayShowHomeEnabled(true);
   }});
    }

}

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.androidactionbartitle.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <Button 
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 1 (background - drawable icon)"/>
    
    <Button 
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 2 (background - color)"/>
    
    <Button 
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 3 (background - null)"/>
    
    <Button 
        android:id="@+id/button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 4 (set icon)"/>
    
    <Button 
        android:id="@+id/button5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Button 5 (set icon null)"/>

</LinearLayout>

Friday, April 10, 2015

Android SoftKeyboard example


It's a Android app example in SDK, to implement SoftKeyboard. It should be in in your Android SDK directory:
<Android SDK>/samples/android-22/legacy/SoftKeyboard

If you import in Android SDK-Eclipse, may be you have to manually set your target SDK:
Right click your project -> Properties -> Select Android on the left, and check Project Build Target of Android 5.1.

Otherwise, error of No resource identifier found for attribute 'supportsSwitchingToNextInputMethod' will be reported in method.xml.

To know more about An input method editor (IME), visit: Creating an Input Method


Android SQLite Essentials

Develop Android applications with one of the most widely used database engines, SQLite

Android SQLite Essentials

About This Book
  • Develop database-driven Android applications and stay ahead of the curve
  • Explore the best techniques to use cursors and loaders to achieve optimum results
  • A step-by-step approach to use SQLite for building Android applications
Who This Book Is For
This is an ideal book for Android programmers who want to explore SQLite databases based on Android applications. The general competency level expected of the reader is prior knowledge of developing applications and basic knowledge of Android and SQL.

In Detail
SQLite is an open source relational database management system. Android uses the SQLite database to store and retrieve data persistently. The driving force behind the platform is the database, enabling a myriad of choices for developers making cutting-edge applications.

Android SQLite Essentials focuses on the core concepts behind building database-driven applications. This book covers the basic and advanced topics with equivalent simplicity and detail, in order to enable readers to quickly grasp and implement the concepts to build an application database.

This book takes a hands-on, example-based approach to help readers understand the core topics of SQLite and Android database-driven applications. This book focuses on providing you with latent as well as widespread knowledge about practices and approaches towards development in an easily understandable manner.

Wednesday, April 8, 2015

Implement custom View to display Animated GIF

It's a improved version of the example "Play animated GIF using android.graphics.Movie, with Movie.decodeStream(InputStream)".


- Add function of Run/Stop, and Repeat setting
- Instead of disable hardware acceleration in AndroidManifest.xml, the custom AnimatedGifView disable hardware acceleration by calling setLayerType(), for API Level 11.

Our custom view, AnimatedGifView.gif
package com.example.androidgif;

import java.io.InputStream;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Movie;
import android.util.AttributeSet;
import android.view.View;

public class AnimatedGifView extends View {

 private InputStream gifInputStream;
 private Movie gifMovie;
 private int movieWidth, movieHeight;
 private long movieDuration;
 private long movieRunDuration;
 private long lastTick;
 private long nowTick;

 private boolean repeat = true;
 private boolean running = true;

 public void setRepeat(boolean r) {
  repeat = r;
 }

 public void setRunning(boolean r) {
  running = r;
 }

 public AnimatedGifView(Context context) {
  super(context);
  init(context);
 }

 public AnimatedGifView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init(context);
 }

 public AnimatedGifView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init(context);
 }

 private void init(Context context) {

  // Turn OFF hardware acceleration
  // API Level 11
  setLayerType(View.LAYER_TYPE_SOFTWARE, null);

  setFocusable(true);
  gifInputStream = context.getResources().openRawResource(
    R.drawable.android_er);

  gifMovie = Movie.decodeStream(gifInputStream);
  movieWidth = gifMovie.width();
  movieHeight = gifMovie.height();
  movieDuration = gifMovie.duration();
 }

 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(movieWidth, movieHeight);
 }

 public int getMovieWidth() {
  return movieWidth;
 }

 public int getMovieHeight() {
  return movieHeight;
 }

 public long getMovieDuration() {
  return movieDuration;
 }

 @Override
 protected void onDraw(Canvas canvas) {
  
  if(gifMovie == null){
   return;
  }

  nowTick = android.os.SystemClock.uptimeMillis();
  if (lastTick == 0) { // first time
   movieRunDuration = 0;
  }else{
   if(running){
    movieRunDuration += nowTick-lastTick;
    if(movieRunDuration > movieDuration){
     if(repeat){
      movieRunDuration = 0;
     }else{
      movieRunDuration = movieDuration;
     }
    }
   }
  }
  
  gifMovie.setTime((int)movieRunDuration);
  gifMovie.draw(canvas, 0, 0);
  
  lastTick = nowTick;
  invalidate();

 }
}

MainActivity.java
package com.example.androidgif;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.TextView;
import android.widget.ToggleButton;

public class MainActivity extends ActionBarActivity {

 TextView textViewInfo;
 AnimatedGifView gifView;
 
 CheckBox cbRepeat;
 ToggleButton tbRun;

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

  gifView = (AnimatedGifView) findViewById(R.id.gifview);
  textViewInfo = (TextView) findViewById(R.id.textinfo);

  String stringInfo = "";
  stringInfo += "Duration: " + gifView.getMovieDuration() + "\n";
  stringInfo += "W x H: " + gifView.getMovieWidth() + " x "
    + gifView.getMovieHeight() + "\n";

  textViewInfo.setText(stringInfo);
  
  cbRepeat = (CheckBox)findViewById(R.id.repeat);
  cbRepeat.setOnCheckedChangeListener(new OnCheckedChangeListener(){

   @Override
   public void onCheckedChanged(CompoundButton buttonView,
     boolean isChecked) {
    gifView.setRepeat(isChecked);
   }});
  
  tbRun = (ToggleButton)findViewById(R.id.run);
  tbRun.setOnCheckedChangeListener(new OnCheckedChangeListener(){

   @Override
   public void onCheckedChanged(CompoundButton buttonView,
     boolean isChecked) {
    gifView.setRunning(isChecked);
   }});
 }

}

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.androidgif.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:autoLink="web"
        android:text="http://android-er.blogspot.com/"
        android:textStyle="bold" />
    
    <CheckBox
        android:id="@+id/repeat"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Repeat"
        android:checked="true"/>
    <ToggleButton
        android:id="@+id/run"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textOn="Stop"
        android:textOff="Run"
        android:checked="true"/>

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/android_er"
            />

        <com.example.androidgif.AnimatedGifView
            android:id="@+id/gifview"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <TextView
        android:id="@+id/textinfo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="info..." />

</LinearLayout>



download filesDownload the files.