Jun 28, 2012

Example of implementing OpenStreetMap on Android using osmdroid

Last article describe the preparation to using osmdroid on Android. It's time to implement a simple app to display OpenStreetMap on Android using osmdroid.

OpenStreetMap on Android

OpenStreetMap on Android

OpenStreetMap on Android


Make sure copy the requested JARs to libs folder, and edit manifest to add the permissions ~ refer last article.

Modify the main layout to add <org.osmdroid.views.MapView>.
<RelativeLayout 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" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:padding="@dimen/padding_medium"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />
    
    <org.osmdroid.views.MapView
        android:id="@+id/mapview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:clickable="true"/>

</RelativeLayout>


Modify the main activity:
package com.android_osmdroid;

import org.osmdroid.DefaultResourceProxyImpl;
import org.osmdroid.ResourceProxy;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.drawable.Drawable;

public class MainActivity extends Activity {
 
 MyItemizedOverlay myItemizedOverlay = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MapView mapView = (MapView) findViewById(R.id.mapview);
        mapView.setBuiltInZoomControls(true);
        
        Drawable marker=getResources().getDrawable(android.R.drawable.star_big_on);
        int markerWidth = marker.getIntrinsicWidth();
        int markerHeight = marker.getIntrinsicHeight();
        marker.setBounds(0, markerHeight, markerWidth, 0);
        
        ResourceProxy resourceProxy = new DefaultResourceProxyImpl(getApplicationContext());
        
        myItemizedOverlay = new MyItemizedOverlay(marker, resourceProxy);
        mapView.getOverlays().add(myItemizedOverlay);
        
        GeoPoint myPoint1 = new GeoPoint(0*1000000, 0*1000000);
        myItemizedOverlay.addItem(myPoint1, "myPoint1", "myPoint1");
        GeoPoint myPoint2 = new GeoPoint(50*1000000, 50*1000000);
        myItemizedOverlay.addItem(myPoint2, "myPoint2", "myPoint2");
        
    } 
    
}


Create a new class MyItemizedOverlay.java.
package com.android_osmdroid;

import java.util.ArrayList;

import org.osmdroid.ResourceProxy;
import org.osmdroid.api.IMapView;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.overlay.ItemizedOverlay;
import org.osmdroid.views.overlay.OverlayItem;

import android.graphics.Point;
import android.graphics.drawable.Drawable;

public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem> {
 
 private ArrayList<OverlayItem> overlayItemList = new ArrayList<OverlayItem>();

 public MyItemizedOverlay(Drawable pDefaultMarker,
   ResourceProxy pResourceProxy) {
  super(pDefaultMarker, pResourceProxy);
  // TODO Auto-generated constructor stub
 }
 
 public void addItem(GeoPoint p, String title, String snippet){
  OverlayItem newItem = new OverlayItem(title, snippet, p);
  overlayItemList.add(newItem);
  populate(); 
 }

 @Override
 public boolean onSnapToItem(int arg0, int arg1, Point arg2, IMapView arg3) {
  // TODO Auto-generated method stub
  return false;
 }

 @Override
 protected OverlayItem createItem(int arg0) {
  // TODO Auto-generated method stub
  return overlayItemList.get(arg0);
 }

 @Override
 public int size() {
  // TODO Auto-generated method stub
  return overlayItemList.size();
 }

}


Compare with Google Map version with the same function.

Next: Display user location by adding MyLocationOverlay on org.osmdroid.views.MapView



Updated@2014-04-16: no map but saw this in my logcat: "Problem downloading MapTile: /0/0/0 HTTP response: HTTP/1.1 403 Forbidden"

The default Apache HttpClient user-agent was recently banned from accessing tile.openstreetmap.org (server returns 403). As a result, the Mapnik provider is no longer serving any tiles for osmdroid.

OpenStreetMap's tile usage policy ( http://wiki.openstreetmap.org/wiki/Tile_usage_policy ) says that a "Valid User-Agent identifying application" must be sent. The current user-agent osmdroid sends when it downloads a tile is "Apache-HttpClient/UNAVAILABLE (java 1.4)" which is pretty generic.

I've created a patch that will send a user-agent that is more compliant with OSM's tile usage policy. The new user-agent is "osmdroid ([package name])" where [package name] is the package name of the application using osmdroid.

The patch can be found here: http://pastebin.com/kxBh1gQ5

The patch is not very pretty, but it does its job.

reference: osmdroid Issue 515 : Send a better user-agent when downloading tiles

Thanks Lora Anneken comment.

Jun 27, 2012

osmdroid - interact with OpenStreetMap to replace Google Map

osmdroid provides Tools/Views to interact with OpenStreetMap-Data. The OpenStreetMapView is a (almost) full/free replacement for Androids MapView class.

To build Android app with osmdroid, your app have to target Android level 3 or higher (1.5 or higher).

Also, you need to download the JARs (currently):

Add the JARs in your Java Build Path:
  • Craete a folder libs in your project.
  • Copy the JARS in the libs folder.
  • Add in Java Build Path: Right click the project -> Properties -> Java Build path, click Add JARs, to add your JARs in libs.

Modify manifest to add the permission:
  • android.permission.ACCESS_COARSE_LOCATION
  • android.permission.ACCESS_FINE_LOCATION
  • android.permission.ACCESS_WIFI_STATE
  • android.permission.ACCESS_NETWORK_STATE
  • android.permission.INTERNET
  • android.permission.WRITE_EXTERNAL_STORAGE

Reference: HowToUseJar

In next articles, I will show how to build basic map app using osmdroid.


Jun 26, 2012

Custom MapView

Refer to my old example of MapView http://android-coding.blogspot.com/2011/06/easy-drawing-current-location-and.html; It's a simple example of using MapView, with everything implement in main activity.

In this article, I'm going to implement a custom MyMapView extends MapView. Most of the routine works will be moved in MyMapView, include MyItemizedOverlay, MyLocationOverlay and some initialization such as setClickable(true), setBuiltInZoomControls(true)...etc. Such that the main activity will become simple and clear.

Custom MapView


Refer to the articles to obtain Maps API Key and MapActivity.

Create a new Android project Target Android 2.3.3 with Google APIs.

Create a new class MyMapView extends MapView. Basically it perform the same works of the example in http://android-coding.blogspot.com/2011/06/easy-drawing-current-location-and.html, with almost everything move in.

package com.AndroidCustomMapView;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.ItemizedOverlay;
import com.google.android.maps.MapView;
import com.google.android.maps.MyLocationOverlay;
import com.google.android.maps.OverlayItem;

public class MyMapView extends MapView {

 MyItemizedOverlay myItemizedOverlay = null;
 MyLocationOverlay myLocationOverlay = null;

 public MyMapView(Context context, String apiKey) {
  super(context, apiKey);
  init(context);
 }

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

 public MyMapView(Context context, AttributeSet attrs, int defStyle) {
  super(context, attrs, defStyle);
  init(context);
 }
 
 private void init(Context ctx){
  setClickable(true);
  setBuiltInZoomControls(true);
  
  Drawable marker=getResources().getDrawable(android.R.drawable.star_big_on);
  int markerWidth = marker.getIntrinsicWidth();
  int markerHeight = marker.getIntrinsicHeight();
  marker.setBounds(0, markerHeight, markerWidth, 0);
  
  myItemizedOverlay = new MyItemizedOverlay(marker);
  getOverlays().add(myItemizedOverlay);
  
  myLocationOverlay = new MyLocationOverlay(ctx, this);
  getOverlays().add(myLocationOverlay);
  
 }
 
 public void addMarker(GeoPoint p, String title, String snippet){
  myItemizedOverlay.addItem(p, title, snippet);
 }
 
 public void callOnResume(){
  myLocationOverlay.enableMyLocation();
  myLocationOverlay.enableCompass();
 }
 
 public void callOnPause(){
  myLocationOverlay.disableMyLocation();
  myLocationOverlay.disableCompass();
 }
 
 public class MyItemizedOverlay extends ItemizedOverlay<OverlayItem> {
  
  private ArrayList<OverlayItem> overlayItemList = new ArrayList<OverlayItem>();

  public MyItemizedOverlay(Drawable defaultMarker) {
   //super(defaultMarker);
   super(boundCenterBottom(defaultMarker));
   populate();
  }

  @Override
  protected OverlayItem createItem(int i) {
   return overlayItemList.get(i);
  }

  @Override
  public int size() {
   return overlayItemList.size();
  }
  
  public void addItem(GeoPoint p, String title, String snippet){
   OverlayItem newItem = new OverlayItem(p, title, snippet);
   overlayItemList.add(newItem);
   populate(); 
  }

 }

}


Modify main.xml to add <com.AndroidCustomMapView.MyMapView> in the layout.
<?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" />
    <com.AndroidCustomMapView.MyMapView
        android:id="@+id/mymapview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:apiKey="--- Your own Maps API Key here ---"/>

</LinearLayout>


Modify the activity, AndroidCustomMapViewActivity to use our MyMapView. It become much clear now.
package com.AndroidCustomMapView;

import android.os.Bundle;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapActivity;

public class AndroidCustomMapViewActivity extends MapActivity {
 
 MyMapView myMapView;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        myMapView = (MyMapView)findViewById(R.id.mymapview);
        
        GeoPoint myPoint1 = new GeoPoint(0*1000000, 0*1000000);
        myMapView.addMarker(myPoint1, "myPoint1", "myPoint1");
        GeoPoint myPoint2 = new GeoPoint(50*1000000, 50*1000000);
        myMapView.addMarker(myPoint2, "myPoint2", "myPoint2");
    }

 @Override
 protected boolean isRouteDisplayed() {
  // TODO Auto-generated method stub
  return false;
 }

 @Override
 protected void onPause() {
  // TODO Auto-generated method stub
  super.onPause();
  myMapView.callOnPause();
 }

 @Override
 protected void onResume() {
  // TODO Auto-generated method stub
  super.onResume();
  myMapView.callOnResume();
 }

}


Remember to modify AndroidManifest.xml to include uses-library of "com.google.android.maps", and grant permission of "android.permission.INTERNET" and "android.permission.ACCESS_FINE_LOCATION".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.AndroidCustomMapView"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <uses-library android:name="com.google.android.maps" />
        <activity
            android:name=".AndroidCustomMapViewActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

</manifest>


Jun 22, 2012

Read Exif info

With ExifInterface, you can read various TAGs from Exif in JPG files; such as APERTURE, EXPOSURE_TIME, FOCAL_LENGTH, ISO, WHITE_BALANCE, FLASH, MODEL, DATETIME, GPS...etc. Please note that the higher target API level support the more TAGs.

Read Exif info


Modify from onItemClick() method of photoBarOnItemClickListener in last post "Implement AdapterView.OnItemClickListener for Gallery" to read Exif TAGS.

 OnItemClickListener photoBarOnItemClickListener
 = new OnItemClickListener(){

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   
   String itemInfo = ((PhotoItem)(parent.getItemAtPosition(position))).bitmapImageSrc;
   
   Toast.makeText(AndroidGalleryActivity.this, 
     "Clicked item:\n" + itemInfo, 
     Toast.LENGTH_LONG).show();
   
   ExifInterface exif = ((PhotoItem)(parent.getItemAtPosition(position))).exifInterface;
   AlertDialog.Builder exifDialog = new AlertDialog.Builder(AndroidGalleryActivity.this);
   exifDialog.setTitle("Exif info:");
      
      String exifInfo = 
        "APERTURE: " + exif.getAttribute(ExifInterface.TAG_APERTURE) + "\n" +
        "EXPOSURE_TIME: " + exif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME) + "\n" +
        "FOCAL_LENGTH: " + exif.getAttribute(ExifInterface.TAG_FOCAL_LENGTH) + "\n" +
        "ISO: " + exif.getAttribute(ExifInterface.TAG_ISO) + "\n" +
        "WHITE_BALANCE: " + exif.getAttribute(ExifInterface.TAG_WHITE_BALANCE) + "\n" +
        "FLASH: " + exif.getAttribute(ExifInterface.TAG_FLASH) + "\n" +
        "MAKE: " + exif.getAttribute(ExifInterface.TAG_MAKE) + "\n" +
        "MODEL: " + exif.getAttribute(ExifInterface.TAG_MODEL) + "\n" +
        "GPS_LATITUDE_REF: " + exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF) + "\n" +
        "GPS_LATITUDE: " + exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE) + "\n" +
        "GPS_LONGITUDE_REF: " + exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF) + "\n" +
        "GPS_LONGITUDE: " + exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE) + "\n" +
        "DATETIME: " + exif.getAttribute(ExifInterface.TAG_DATETIME);
      
      TextView textExifInfo = new TextView(AndroidGalleryActivity.this);
      textExifInfo.setText(exifInfo);
      LayoutParams textOutLayoutParams 
       = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
      textExifInfo.setLayoutParams(textOutLayoutParams);
      
      LinearLayout dialogLayout = new LinearLayout(AndroidGalleryActivity.this);
      dialogLayout.setOrientation(LinearLayout.VERTICAL);
      dialogLayout.addView(textExifInfo);
      exifDialog.setView(dialogLayout);
      
      exifDialog.setPositiveButton("OK", null);
      
      exifDialog.show();

  }
  
 };


Jun 21, 2012

Implement AdapterView.OnItemClickListener for Gallery

Modify last article "Get Exif of jpg using ExifInterface, and thumbnail inside the JPEG" to implement AdapterView.OnItemClickListener for the Gallery. When user touch on any item, the callback method onItemClick (AdapterView<?> parent, View view, int position, long id) will be called. To access the data associated with the selected item, call parent.getItemAtPosition(position).

Implement AdapterView.OnItemClickListener for Gallery


package com.AndroidGallery;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import android.R.color;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidGalleryActivity extends Activity {
 
 Gallery photoBar;
 
 Button buttonOpenDialog;
 Button buttonUp, buttonSelectFolder;
 TextView jpgList;
   
 String KEY_TEXTPSS = "TEXTPSS";
 static final int CUSTOM_DIALOG_ID = 0;
 
 ListView dialog_ListView;
 
 File root;
 File curFolder;
 
 private List<String> fileList = new ArrayList<String>();
 
 class PhotoItem{
  
  ExifInterface exifInterface = null;
  boolean exifHasThumbnail = false;
  
  String bitmapImageSrc;
  Bitmap bitmapImage = null;
  
  final static int itemWidth = 150;
  final static int itemHeight = 150;
  
  Handler handler;
  
  public PhotoItem(String src){
   bitmapImageSrc = src;
   StratBackgroundProcess();
   
   bitmapImage = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_gallery);
  }
  
  public Bitmap getImage(){
   return bitmapImage; 
  }
  
  private void runResize(){
   BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
   bmpFactoryOptions.inJustDecodeBounds = true;

   Bitmap bitmap = BitmapFactory.decodeFile(bitmapImageSrc, bmpFactoryOptions);
   
   int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)itemHeight);
   int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)itemWidth);
   
   if (heightRatio > 1 || widthRatio > 1)
   {
    if (heightRatio > widthRatio){
     bmpFactoryOptions.inSampleSize = heightRatio; 
    } else {
     bmpFactoryOptions.inSampleSize = widthRatio;  
    }  
   }
             
   bmpFactoryOptions.inJustDecodeBounds = false;
   
   bitmapImage = BitmapFactory.decodeFile(bitmapImageSrc, bmpFactoryOptions);

  }
  
  private void getExif(){
   
   try {
    exifInterface = new ExifInterface(bitmapImageSrc);
    if(exifInterface.hasThumbnail()){
     byte[] thumbnail =  exifInterface.getThumbnail();
     bitmapImage = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
     exifHasThumbnail = true;
    }
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }

  }
  
  private void StratBackgroundProcess(){
   Runnable runnable = new Runnable(){

    @Override
    public void run() {

     getExif();
     if(!exifHasThumbnail){
      runResize();
     }
     
     handler.post(new Runnable(){

      @Override
      public void run() {
       myPhotoBarAdapter.notifyDataSetChanged();
      }});
     
    }
    
   };
   
   handler = new Handler();
   new Thread(runnable).start();
  }
  
 }
 
 PhotoBarAdapter myPhotoBarAdapter;
 
 public class PhotoBarAdapter extends BaseAdapter {
  
  Context context;
  ArrayList<PhotoItem> arrayPhotoItem;

  PhotoBarAdapter(Context c){
      context = c;
      arrayPhotoItem = new ArrayList<PhotoItem>();    
  }
  
  public void clear(){
   arrayPhotoItem.clear();
  }
  
  public void addPhotoItem(PhotoItem item){
   arrayPhotoItem.add(item);    
  }
  
  @Override
  public int getCount() {
   return arrayPhotoItem.size();
  }

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

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

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   
   LinearLayout viewLayout = new LinearLayout(context);
   viewLayout.setLayoutParams(new Gallery.LayoutParams(200, 200));
   viewLayout.setGravity(Gravity.CENTER);
   viewLayout.setBackgroundColor(color.background_light);
   
   ImageView imageView;
   imageView = new ImageView(context);
   imageView.setLayoutParams(new Gallery.LayoutParams(150, 150));
   imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
   imageView.setImageBitmap(arrayPhotoItem.get(position).getImage());
   //return imageView;
   
   viewLayout.addView(imageView);
   return viewLayout;
  }
  
 }
 
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  photoBar = (Gallery)findViewById(R.id.photobar);
  myPhotoBarAdapter = new PhotoBarAdapter(this);
  photoBar.setAdapter(myPhotoBarAdapter);
   
  jpgList = (TextView)findViewById(R.id.jpglist);
   
  buttonOpenDialog = (Button)findViewById(R.id.opendialog);
  buttonOpenDialog.setOnClickListener(new Button.OnClickListener(){
   @Override
   public void onClick(View arg0) {
    showDialog(CUSTOM_DIALOG_ID); 
   }});
   
  root = new File(Environment
    .getExternalStorageDirectory()
    .getAbsolutePath());
   
  curFolder = root;
  //ListJpgInFolder(curFolder);
  preparePhotoBarInFolder(curFolder);
  
  //Gallery onClickListener
  photoBar.setOnItemClickListener(photoBarOnItemClickListener);
   
 }
 
 OnItemClickListener photoBarOnItemClickListener
 = new OnItemClickListener(){

  @Override
  public void onItemClick(AdapterView<?> parent, View view, int position,
    long id) {
   
   String itemInfo = ((PhotoItem)(parent.getItemAtPosition(position))).bitmapImageSrc;
   
   Toast.makeText(AndroidGalleryActivity.this, 
     "Clicked item:\n" + itemInfo, 
     Toast.LENGTH_LONG).show();
   
  }
  
 };

  
 @Override
 protected Dialog onCreateDialog(int id) {
   
  Dialog dialog = null;
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   dialog = new Dialog(AndroidGalleryActivity.this);
   dialog.setContentView(R.layout.dialoglayout);
   dialog.setTitle("Select JPG");
    
   dialog.setCancelable(true);
   dialog.setCanceledOnTouchOutside(true);
    
   buttonUp = (Button)dialog.findViewById(R.id.up);
   buttonUp.setOnClickListener(new OnClickListener(){
     
    @Override
    public void onClick(View v) {

     ListDir(curFolder.getParentFile()); 
    }});
   
   buttonSelectFolder = (Button)dialog.findViewById(R.id.selectfolder);
   buttonSelectFolder.setOnClickListener(new OnClickListener(){
    
   @Override
   public void onClick(View v) {

    Toast.makeText(AndroidGalleryActivity.this,
      curFolder + " selected",
      Toast.LENGTH_LONG).show();
    dismissDialog(CUSTOM_DIALOG_ID);
    
    //ListJpgInFolder(curFolder);
    preparePhotoBarInFolder(curFolder);

   }});
   
    
   //Prepare ListView in dialog
   dialog_ListView = (ListView)dialog.findViewById(R.id.dialoglist);
    
   dialog_ListView.setOnItemClickListener(new OnItemClickListener(){
     
    @Override
    public void onItemClick(AdapterView<?> parent, View view,
      int position, long id) {
     File selected = new File(fileList.get(position));
     if(selected.isDirectory()){
      ListDir(selected);  
     } 
    }});
        
   break;
  }
  return dialog; 
 }
  
 @Override
 protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {

  super.onPrepareDialog(id, dialog, bundle);
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   ListDir(curFolder);
   
   break; 
  } 
 }
  
 void ListDir(File f){
   
  if(f.equals(root)){
   buttonUp.setEnabled(false); 
  }else{
   buttonUp.setEnabled(true); 
  }
   
  curFolder = f;
  buttonSelectFolder.setText("Select Folder " + curFolder);
   
  File[] files = f.listFiles();
  fileList.clear();
  for (File file : files){
   if(file.isDirectory()){
    fileList.add(file.getPath()); 
   }else{
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     fileList.add(file.getName()); 
    } 
   } 
  }
   
  ArrayAdapter<String> directoryList
   = new ArrayAdapter<String>(this,
     android.R.layout.simple_list_item_1, fileList);
  dialog_ListView.setAdapter(directoryList); 
 }
 
 private void preparePhotoBarInFolder(File folder){
  
  String jpgs = "JPG fiels in folder " + folder.getAbsolutePath() + "\n\n";
  
  File[] files = folder.listFiles();
  
  myPhotoBarAdapter.clear();
  
  for (File file : files){
   if(!file.isDirectory()){
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     jpgs += file.getAbsolutePath() + "\n";
     
     PhotoItem pItem = new PhotoItem(file.getAbsolutePath());
     myPhotoBarAdapter.addPhotoItem(pItem);
     
     //Forcer myPhotoBarAdapter update when each photos added.
     myPhotoBarAdapter.notifyDataSetChanged();
    }  
   }  
  }
  
  //Forcer myPhotoBarAdapter update again even without photo.
  myPhotoBarAdapter.notifyDataSetChanged();
  
  jpgList.setText(jpgs); 
 }
  
}


Next:
- Read Exif info


Jun 20, 2012

Get Exif of jpg using ExifInterface, and thumbnail inside the JPEG.

android.media.ExifInterface is a class for reading and writing Exif tags in a JPEG file. The constructor ExifInterface(String filename) reads Exif tags from the specified JPEG file.

The method hasThumbnail() check if the JPEG file has a thumbnail. And the method getThumbnail() returns the thumbnail inside the JPEG file, or null if there is no thumbnail.

Get Exif of jpg using ExifInterface, and thumbnail inside the JPEG.


Refer to the last example "Update Gallery photos in background thread"; getExif() method is added in PhotoItem class of AndroidGalleryActivity.java, To check and load thumbnail. If it has thumbnail in the JPG file, the original runResize() will be skipped. With this implementation, the display time is much shorten for large picture.

package com.AndroidGallery;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import android.R.color;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidGalleryActivity extends Activity {
 
 Gallery photoBar;
 
 Button buttonOpenDialog;
 Button buttonUp, buttonSelectFolder;
 TextView jpgList;
   
 String KEY_TEXTPSS = "TEXTPSS";
 static final int CUSTOM_DIALOG_ID = 0;
 
 ListView dialog_ListView;
 
 File root;
 File curFolder;
 
 private List<String> fileList = new ArrayList<String>();
 
 class PhotoItem{
  
  ExifInterface exifInterface = null;
  boolean exifHasThumbnail = false;
  
  String bitmapImageSrc;
  Bitmap bitmapImage = null;
  
  final static int itemWidth = 150;
  final static int itemHeight = 150;
  
  Handler handler;
  
  public PhotoItem(String src){
   bitmapImageSrc = src;
   StratBackgroundProcess();
   
   bitmapImage = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_gallery);
  }
  
  public Bitmap getImage(){
   return bitmapImage; 
  }
  
  private void runResize(){
   BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
   bmpFactoryOptions.inJustDecodeBounds = true;

   Bitmap bitmap = BitmapFactory.decodeFile(bitmapImageSrc, bmpFactoryOptions);
   
   int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)itemHeight);
   int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)itemWidth);
   
   if (heightRatio > 1 || widthRatio > 1)
   {
    if (heightRatio > widthRatio){
     bmpFactoryOptions.inSampleSize = heightRatio; 
    } else {
     bmpFactoryOptions.inSampleSize = widthRatio;  
    }  
   }
             
   bmpFactoryOptions.inJustDecodeBounds = false;
   
   bitmapImage = BitmapFactory.decodeFile(bitmapImageSrc, bmpFactoryOptions);

  }
  
  private void getExif(){
   
   try {
    exifInterface = new ExifInterface(bitmapImageSrc);
    if(exifInterface.hasThumbnail()){
     byte[] thumbnail =  exifInterface.getThumbnail();
     bitmapImage = BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length);
     exifHasThumbnail = true;
    }
   } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }

  }
  
  private void StratBackgroundProcess(){
   Runnable runnable = new Runnable(){

    @Override
    public void run() {

     getExif();
     if(!exifHasThumbnail){
      runResize();
     }
     
     handler.post(new Runnable(){

      @Override
      public void run() {
       myPhotoBarAdapter.notifyDataSetChanged();
      }});
     
    }
    
   };
   
   handler = new Handler();
   new Thread(runnable).start();
  }
  
 }
 
 PhotoBarAdapter myPhotoBarAdapter;
 
 public class PhotoBarAdapter extends BaseAdapter {
  
  Context context;
  ArrayList<PhotoItem> arrayPhotoItem;

  PhotoBarAdapter(Context c){
      context = c;
      arrayPhotoItem = new ArrayList<PhotoItem>();    
  }
  
  public void clear(){
   arrayPhotoItem.clear();
  }
  
  public void addPhotoItem(PhotoItem item){
   arrayPhotoItem.add(item);    
  }
  
  @Override
  public int getCount() {
   return arrayPhotoItem.size();
  }

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

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

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   
   LinearLayout viewLayout = new LinearLayout(context);
   viewLayout.setLayoutParams(new Gallery.LayoutParams(200, 200));
   viewLayout.setGravity(Gravity.CENTER);
   viewLayout.setBackgroundColor(color.background_light);
   
   ImageView imageView;
   imageView = new ImageView(context);
   imageView.setLayoutParams(new Gallery.LayoutParams(150, 150));
   imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
   imageView.setImageBitmap(arrayPhotoItem.get(position).getImage());
   //return imageView;
   
   viewLayout.addView(imageView);
   return viewLayout;
  }
  
 }
 
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  photoBar = (Gallery)findViewById(R.id.photobar);
  myPhotoBarAdapter = new PhotoBarAdapter(this);
  photoBar.setAdapter(myPhotoBarAdapter);
   
  jpgList = (TextView)findViewById(R.id.jpglist);
   
  buttonOpenDialog = (Button)findViewById(R.id.opendialog);
  buttonOpenDialog.setOnClickListener(new Button.OnClickListener(){
   @Override
   public void onClick(View arg0) {
    showDialog(CUSTOM_DIALOG_ID); 
   }});
   
  root = new File(Environment
    .getExternalStorageDirectory()
    .getAbsolutePath());
   
  curFolder = root;
  //ListJpgInFolder(curFolder);
  preparePhotoBarInFolder(curFolder);
   
 }
  
 @Override
 protected Dialog onCreateDialog(int id) {
   
  Dialog dialog = null;
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   dialog = new Dialog(AndroidGalleryActivity.this);
   dialog.setContentView(R.layout.dialoglayout);
   dialog.setTitle("Select JPG");
    
   dialog.setCancelable(true);
   dialog.setCanceledOnTouchOutside(true);
    
   buttonUp = (Button)dialog.findViewById(R.id.up);
   buttonUp.setOnClickListener(new OnClickListener(){
     
    @Override
    public void onClick(View v) {

     ListDir(curFolder.getParentFile()); 
    }});
   
   buttonSelectFolder = (Button)dialog.findViewById(R.id.selectfolder);
   buttonSelectFolder.setOnClickListener(new OnClickListener(){
    
   @Override
   public void onClick(View v) {

    Toast.makeText(AndroidGalleryActivity.this,
      curFolder + " selected",
      Toast.LENGTH_LONG).show();
    dismissDialog(CUSTOM_DIALOG_ID);
    
    //ListJpgInFolder(curFolder);
    preparePhotoBarInFolder(curFolder);

   }});
   
    
   //Prepare ListView in dialog
   dialog_ListView = (ListView)dialog.findViewById(R.id.dialoglist);
    
   dialog_ListView.setOnItemClickListener(new OnItemClickListener(){
     
    @Override
    public void onItemClick(AdapterView<?> parent, View view,
      int position, long id) {
     File selected = new File(fileList.get(position));
     if(selected.isDirectory()){
      ListDir(selected);  
     } 
    }});
        
   break;
  }
  return dialog; 
 }
  
 @Override
 protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {

  super.onPrepareDialog(id, dialog, bundle);
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   ListDir(curFolder);
   
   break; 
  } 
 }
  
 void ListDir(File f){
   
  if(f.equals(root)){
   buttonUp.setEnabled(false); 
  }else{
   buttonUp.setEnabled(true); 
  }
   
  curFolder = f;
  buttonSelectFolder.setText("Select Folder " + curFolder);
   
  File[] files = f.listFiles();
  fileList.clear();
  for (File file : files){
   if(file.isDirectory()){
    fileList.add(file.getPath()); 
   }else{
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     fileList.add(file.getName()); 
    } 
   } 
  }
   
  ArrayAdapter<String> directoryList
   = new ArrayAdapter<String>(this,
     android.R.layout.simple_list_item_1, fileList);
  dialog_ListView.setAdapter(directoryList); 
 }
 
 private void preparePhotoBarInFolder(File folder){
  
  String jpgs = "JPG fiels in folder " + folder.getAbsolutePath() + "\n\n";
  
  File[] files = folder.listFiles();
  
  myPhotoBarAdapter.clear();
  
  for (File file : files){
   if(!file.isDirectory()){
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     jpgs += file.getAbsolutePath() + "\n";
     
     PhotoItem pItem = new PhotoItem(file.getAbsolutePath());
     myPhotoBarAdapter.addPhotoItem(pItem);
     
     //Forcer myPhotoBarAdapter update when each photos added.
     myPhotoBarAdapter.notifyDataSetChanged();
    }  
   }  
  }
  
  //Forcer myPhotoBarAdapter update again even without photo.
  myPhotoBarAdapter.notifyDataSetChanged();
  
  jpgList.setText(jpgs); 
 }
  
}


Next:
- Implement AdapterView.OnItemClickListener for Gallery


Jun 18, 2012

Update Gallery photos in background thread

Last article "Display photos from SD in android.widget.Gallery" load Gallery photos in UI thread. As I mentioned in some previous posts, it's not a good practice to perform long time operation in UI thread, specially when you load many large size photos.

Update Gallery photos in background thread


The main code, AndroidGalleryActivity.java, is further modified to perform the re-size job in background thread. Before the photos re-sized, the Gallery show items as a default bitmap (android.R.drawable.ic_menu_gallery). After re-size, myPhotoBarAdapter.notifyDataSetChanged() is called (in UI thread) to update Gallery.

Because every photo have its own background thread to re-size, and independent to each others. So the update sequence is not in order.

package com.AndroidGallery;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.R.color;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidGalleryActivity extends Activity {
 
 Gallery photoBar;
 
 Button buttonOpenDialog;
 Button buttonUp, buttonSelectFolder;
 TextView jpgList;
   
 String KEY_TEXTPSS = "TEXTPSS";
 static final int CUSTOM_DIALOG_ID = 0;
 
 ListView dialog_ListView;
 
 File root;
 File curFolder;
 
 private List<String> fileList = new ArrayList<String>();
 
 class PhotoItem{
  
  String bitmapImageSrc;
  Bitmap bitmapImage = null;
  
  final static int itemWidth = 150;
  final static int itemHeight = 150;
  
  Handler handler;
  
  public PhotoItem(String src){
   bitmapImageSrc = src;
   StratBackgroundProcess();
   
   bitmapImage = BitmapFactory.decodeResource(getResources(), android.R.drawable.ic_menu_gallery);
  }
  
  public Bitmap getImage(){
   return bitmapImage; 
  }
  
  private void runResize(){
   BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
   bmpFactoryOptions.inJustDecodeBounds = true;

   Bitmap bitmap = BitmapFactory.decodeFile(bitmapImageSrc, bmpFactoryOptions);
   
   int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)itemHeight);
   int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)itemWidth);
   
   if (heightRatio > 1 || widthRatio > 1)
   {
    if (heightRatio > widthRatio){
     bmpFactoryOptions.inSampleSize = heightRatio; 
    } else {
     bmpFactoryOptions.inSampleSize = widthRatio;  
    }  
   }
             
   bmpFactoryOptions.inJustDecodeBounds = false;
   
   bitmapImage = BitmapFactory.decodeFile(bitmapImageSrc, bmpFactoryOptions);

  }
  
  private void StratBackgroundProcess(){
   Runnable runnable = new Runnable(){

    @Override
    public void run() {

     runResize();
     
     handler.post(new Runnable(){

      @Override
      public void run() {
       myPhotoBarAdapter.notifyDataSetChanged();
      }});
     
    }
    
   };
   
   handler = new Handler();
   new Thread(runnable).start();
  }
  
 }
 
 PhotoBarAdapter myPhotoBarAdapter;
 
 public class PhotoBarAdapter extends BaseAdapter {
  
  Context context;
  ArrayList<PhotoItem> arrayPhotoItem;

  PhotoBarAdapter(Context c){
      context = c;
      arrayPhotoItem = new ArrayList<PhotoItem>();    
  }
  
  public void clear(){
   arrayPhotoItem.clear();
  }
  
  public void addPhotoItem(PhotoItem item){
   arrayPhotoItem.add(item);    
  }
  
  @Override
  public int getCount() {
   return arrayPhotoItem.size();
  }

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

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

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   
   LinearLayout viewLayout = new LinearLayout(context);
   viewLayout.setLayoutParams(new Gallery.LayoutParams(200, 200));
   viewLayout.setGravity(Gravity.CENTER);
   viewLayout.setBackgroundColor(color.background_light);
   
   ImageView imageView;
   imageView = new ImageView(context);
   imageView.setLayoutParams(new Gallery.LayoutParams(150, 150));
   imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
   imageView.setImageBitmap(arrayPhotoItem.get(position).getImage());
   //return imageView;
   
   viewLayout.addView(imageView);
   return viewLayout;
  }
  
 }
 
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  photoBar = (Gallery)findViewById(R.id.photobar);
  myPhotoBarAdapter = new PhotoBarAdapter(this);
  photoBar.setAdapter(myPhotoBarAdapter);
   
  jpgList = (TextView)findViewById(R.id.jpglist);
   
  buttonOpenDialog = (Button)findViewById(R.id.opendialog);
  buttonOpenDialog.setOnClickListener(new Button.OnClickListener(){
   @Override
   public void onClick(View arg0) {
    showDialog(CUSTOM_DIALOG_ID); 
   }});
   
  root = new File(Environment
    .getExternalStorageDirectory()
    .getAbsolutePath());
   
  curFolder = root;
  //ListJpgInFolder(curFolder);
  preparePhotoBarInFolder(curFolder);
   
 }
  
 @Override
 protected Dialog onCreateDialog(int id) {
   
  Dialog dialog = null;
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   dialog = new Dialog(AndroidGalleryActivity.this);
   dialog.setContentView(R.layout.dialoglayout);
   dialog.setTitle("Select JPG");
    
   dialog.setCancelable(true);
   dialog.setCanceledOnTouchOutside(true);
    
   buttonUp = (Button)dialog.findViewById(R.id.up);
   buttonUp.setOnClickListener(new OnClickListener(){
     
    @Override
    public void onClick(View v) {

     ListDir(curFolder.getParentFile()); 
    }});
   
   buttonSelectFolder = (Button)dialog.findViewById(R.id.selectfolder);
   buttonSelectFolder.setOnClickListener(new OnClickListener(){
    
   @Override
   public void onClick(View v) {

    Toast.makeText(AndroidGalleryActivity.this,
      curFolder + " selected",
      Toast.LENGTH_LONG).show();
    dismissDialog(CUSTOM_DIALOG_ID);
    
    //ListJpgInFolder(curFolder);
    preparePhotoBarInFolder(curFolder);

   }});
   
    
   //Prepare ListView in dialog
   dialog_ListView = (ListView)dialog.findViewById(R.id.dialoglist);
    
   dialog_ListView.setOnItemClickListener(new OnItemClickListener(){
     
    @Override
    public void onItemClick(AdapterView<?> parent, View view,
      int position, long id) {
     File selected = new File(fileList.get(position));
     if(selected.isDirectory()){
      ListDir(selected);  
     } 
    }});
        
   break;
  }
  return dialog; 
 }
  
 @Override
 protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {

  super.onPrepareDialog(id, dialog, bundle);
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   ListDir(curFolder);
   
   break; 
  } 
 }
  
 void ListDir(File f){
   
  if(f.equals(root)){
   buttonUp.setEnabled(false); 
  }else{
   buttonUp.setEnabled(true); 
  }
   
  curFolder = f;
  buttonSelectFolder.setText("Select Folder " + curFolder);
   
  File[] files = f.listFiles();
  fileList.clear();
  for (File file : files){
   if(file.isDirectory()){
    fileList.add(file.getPath()); 
   }else{
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     fileList.add(file.getName()); 
    } 
   } 
  }
   
  ArrayAdapter<String> directoryList
   = new ArrayAdapter<String>(this,
     android.R.layout.simple_list_item_1, fileList);
  dialog_ListView.setAdapter(directoryList); 
 }
 
 private void preparePhotoBarInFolder(File folder){
  
  String jpgs = "JPG fiels in folder " + folder.getAbsolutePath() + "\n\n";
  
  File[] files = folder.listFiles();
  
  myPhotoBarAdapter.clear();
  
  for (File file : files){
   if(!file.isDirectory()){
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     jpgs += file.getAbsolutePath() + "\n";
     
     PhotoItem pItem = new PhotoItem(file.getAbsolutePath());
     myPhotoBarAdapter.addPhotoItem(pItem);
     
     myPhotoBarAdapter.notifyDataSetChanged();
    }  
   }  
  }
  jpgList.setText(jpgs); 
 }
  
}


Next:
- Get Exif of jpg using ExifInterface, and thumbnail inside the JPEG.


Jun 14, 2012

Display photos from SD in android.widget.Gallery

Last example demonstrate how to "Implement custom dialog to open folder". The photos in the selected folder will be loaded in a custom adapter to display in Gallery widget.

Display photos from SD in Gallery widget


The layout of the dialog is same as previous example, refer dialoglayout.xml in "Implement custom dialog to open folder".

Modify main.xml, to add <Gallery>.
<?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" />
    
    <Gallery
        android:id="@+id/photobar"
        android:layout_width="fill_parent"
        android:layout_height="200px" />
 
    <Button
        android:id="@+id/opendialog"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Load Folder" />
    <TextView
        android:id="@+id/jpglist"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>


Modify AndroidGalleryActivity.java.

In order to reduce the resource need to keep the bitmaps, we have to implement a new class PhotoItem hold the path of individual jpg file, and the re-sized bitmap. And a custom BaseAdapter, PhotoBarAdapter, for PhotoItem is implemented for the Gallery.

package com.AndroidGallery;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.R.color;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.webkit.MimeTypeMap;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.Gallery;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidGalleryActivity extends Activity {
 
 Gallery photoBar;
 
 Button buttonOpenDialog;
 Button buttonUp, buttonSelectFolder;
 TextView jpgList;
   
 String KEY_TEXTPSS = "TEXTPSS";
 static final int CUSTOM_DIALOG_ID = 0;
 
 ListView dialog_ListView;
 
 File root;
 File curFolder;
 
 private List<String> fileList = new ArrayList<String>();
 
 class PhotoItem{
  
  String bitmapImageSrc;
  Bitmap bitmapImage = null;
  
  final static int itemWidth = 150;
  final static int itemHeight = 150;
  
  public PhotoItem(String src){
   bitmapImageSrc = src;
   bitmapImage = resize();
  }
  
  public Bitmap getImage(){
   return bitmapImage; 
  }
  
  private Bitmap resize(){
   BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options();
   bmpFactoryOptions.inJustDecodeBounds = true;

   Bitmap bitmap = BitmapFactory.decodeFile(bitmapImageSrc, bmpFactoryOptions);
   
   int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)itemHeight);
   int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)itemWidth);
   
   if (heightRatio > 1 || widthRatio > 1)
   {
    if (heightRatio > widthRatio){
     bmpFactoryOptions.inSampleSize = heightRatio; 
    } else {
     bmpFactoryOptions.inSampleSize = widthRatio;  
    }  
   }
             
   bmpFactoryOptions.inJustDecodeBounds = false;
   
   bitmap = BitmapFactory.decodeFile(bitmapImageSrc, bmpFactoryOptions);
   
   return bitmap; 
  }
 }
 
 PhotoBarAdapter myPhotoBarAdapter;
 
 public class PhotoBarAdapter extends BaseAdapter {
  
  Context context;
  ArrayList<PhotoItem> arrayPhotoItem;

  
  PhotoBarAdapter(Context c){
      context = c;
      arrayPhotoItem = new ArrayList<PhotoItem>();    
  }
  
  public void clear(){
   arrayPhotoItem.clear();
  }
  
  public void addPhotoItem(PhotoItem item){
   arrayPhotoItem.add(item);    
  }
  
  @Override
  public int getCount() {
   return arrayPhotoItem.size();
  }

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

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

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
   
   LinearLayout viewLayout = new LinearLayout(context);
   viewLayout.setLayoutParams(new Gallery.LayoutParams(200, 200));
   viewLayout.setBackgroundColor(color.background_light);
   
   ImageView imageView;
   imageView = new ImageView(context);
   imageView.setLayoutParams(new Gallery.LayoutParams(150, 150));
   imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
   imageView.setImageBitmap(arrayPhotoItem.get(position).getImage());
   //return imageView;
   
   viewLayout.addView(imageView);
   return viewLayout;
  }
  
 }
 
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  photoBar = (Gallery)findViewById(R.id.photobar);
  myPhotoBarAdapter = new PhotoBarAdapter(this);
  photoBar.setAdapter(myPhotoBarAdapter);
   
  jpgList = (TextView)findViewById(R.id.jpglist);
   
  buttonOpenDialog = (Button)findViewById(R.id.opendialog);
  buttonOpenDialog.setOnClickListener(new Button.OnClickListener(){
   @Override
   public void onClick(View arg0) {
    showDialog(CUSTOM_DIALOG_ID); 
   }});
   
  root = new File(Environment
    .getExternalStorageDirectory()
    .getAbsolutePath());
   
  curFolder = root;
  //ListJpgInFolder(curFolder);
  preparePhotoBarInFolder(curFolder);
   
 }
  
 @Override
 protected Dialog onCreateDialog(int id) {
   
  Dialog dialog = null;
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   dialog = new Dialog(AndroidGalleryActivity.this);
   dialog.setContentView(R.layout.dialoglayout);
   dialog.setTitle("Select JPG");
    
   dialog.setCancelable(true);
   dialog.setCanceledOnTouchOutside(true);
    
   buttonUp = (Button)dialog.findViewById(R.id.up);
   buttonUp.setOnClickListener(new OnClickListener(){
     
    @Override
    public void onClick(View v) {
     // TODO Auto-generated method stub
     ListDir(curFolder.getParentFile()); 
    }});
   
   buttonSelectFolder = (Button)dialog.findViewById(R.id.selectfolder);
   buttonSelectFolder.setOnClickListener(new OnClickListener(){
    
   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    Toast.makeText(AndroidGalleryActivity.this,
      curFolder + " selected",
      Toast.LENGTH_LONG).show();
    dismissDialog(CUSTOM_DIALOG_ID);
    
    //ListJpgInFolder(curFolder);
    preparePhotoBarInFolder(curFolder);

   }});
   
    
   //Prepare ListView in dialog
   dialog_ListView = (ListView)dialog.findViewById(R.id.dialoglist);
    
   dialog_ListView.setOnItemClickListener(new OnItemClickListener(){
     
    @Override
    public void onItemClick(AdapterView<?> parent, View view,
      int position, long id) {
     File selected = new File(fileList.get(position));
     if(selected.isDirectory()){
      ListDir(selected);  
     } 
    }});
        
   break;
  }
  return dialog; 
 }
  
 @Override
 protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
  // TODO Auto-generated method stub
  super.onPrepareDialog(id, dialog, bundle);
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   ListDir(curFolder);
   
   break; 
  } 
 }
  
 void ListDir(File f){
   
  if(f.equals(root)){
   buttonUp.setEnabled(false); 
  }else{
   buttonUp.setEnabled(true); 
  }
   
  curFolder = f;
  buttonSelectFolder.setText("Select Folder " + curFolder);
   
  File[] files = f.listFiles();
  fileList.clear();
  for (File file : files){
   if(file.isDirectory()){
    fileList.add(file.getPath()); 
   }else{
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     fileList.add(file.getName()); 
    } 
   } 
  }
   
  ArrayAdapter<String> directoryList
   = new ArrayAdapter<String>(this,
     android.R.layout.simple_list_item_1, fileList);
  dialog_ListView.setAdapter(directoryList); 
 }
 
 private void preparePhotoBarInFolder(File folder){
  
  String jpgs = "JPG fiels in folder " + folder.getAbsolutePath() + "\n\n";
  
  File[] files = folder.listFiles();
  
  myPhotoBarAdapter.clear();
  
  for (File file : files){
   if(!file.isDirectory()){
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     jpgs += file.getAbsolutePath() + "\n";
     
     PhotoItem pItem = new PhotoItem(file.getAbsolutePath());
     myPhotoBarAdapter.addPhotoItem(pItem);
     
     myPhotoBarAdapter.notifyDataSetChanged();
    }  
   }  
  }
  jpgList.setText(jpgs); 
 }
  
}


Next:
- Update Gallery photos in background thread


Implement custom dialog to open folder

In the old article "JPG File Chooser", custom dialog was implemented to load individual image(jpg). In this article, the custom dialog is used to open folder, instead of individual file. After dismissed, the images(jpg) in the selected folder will be listed.

custom dialog to open folder


dialoglayout.xml, layout of the custom dislog.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/customdialog"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="20dp"
    android:minWidth="300dp">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher"/>
        <Button
            android:id="@+id/up"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Parent folder"/>
    </LinearLayout>
    <Button
        android:id="@+id/selectfolder"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>
         
    <ListView
        android:id="@+id/dialoglist"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
 
</LinearLayout>


AndroidGalleryActivity.java, the main code.
package com.AndroidGallery;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.Dialog;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.MimeTypeMap;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidGalleryActivity extends Activity {
 
 Button buttonOpenDialog;
 Button buttonUp, buttonSelectFolder;
 TextView jpgList;
   
 String KEY_TEXTPSS = "TEXTPSS";
 static final int CUSTOM_DIALOG_ID = 0;
 
 ListView dialog_ListView;
 
 File root;
 File curFolder;
 
 private List<String> fileList = new ArrayList<String>();
 
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
   
  jpgList = (TextView)findViewById(R.id.jpglist);
   
  buttonOpenDialog = (Button)findViewById(R.id.opendialog);
  buttonOpenDialog.setOnClickListener(new Button.OnClickListener(){
   @Override
   public void onClick(View arg0) {
    showDialog(CUSTOM_DIALOG_ID); 
   }});
   
  root = new File(Environment
    .getExternalStorageDirectory()
    .getAbsolutePath());
   
  curFolder = root;
  ListJpgInFolder(curFolder);
   
 }
  
 @Override
 protected Dialog onCreateDialog(int id) {
   
  Dialog dialog = null;
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   dialog = new Dialog(AndroidGalleryActivity.this);
   dialog.setContentView(R.layout.dialoglayout);
   dialog.setTitle("Select JPG");
    
   dialog.setCancelable(true);
   dialog.setCanceledOnTouchOutside(true);
    
   buttonUp = (Button)dialog.findViewById(R.id.up);
   buttonUp.setOnClickListener(new OnClickListener(){
     
    @Override
    public void onClick(View v) {
     // TODO Auto-generated method stub
     ListDir(curFolder.getParentFile()); 
    }});
   
   buttonSelectFolder = (Button)dialog.findViewById(R.id.selectfolder);
   buttonSelectFolder.setOnClickListener(new OnClickListener(){
    
   @Override
   public void onClick(View v) {
    // TODO Auto-generated method stub
    Toast.makeText(AndroidGalleryActivity.this,
      curFolder + " selected",
      Toast.LENGTH_LONG).show();
    dismissDialog(CUSTOM_DIALOG_ID);
    
    ListJpgInFolder(curFolder);

   }});
   
    
   //Prepare ListView in dialog
   dialog_ListView = (ListView)dialog.findViewById(R.id.dialoglist);
    
   dialog_ListView.setOnItemClickListener(new OnItemClickListener(){
     
    @Override
    public void onItemClick(AdapterView<?> parent, View view,
      int position, long id) {
     File selected = new File(fileList.get(position));
     if(selected.isDirectory()){
      ListDir(selected);  
     }else {
      /*
      Toast.makeText(AndroidGalleryActivity.this,
        selected.toString() + " selected",
        Toast.LENGTH_LONG).show();
      dismissDialog(CUSTOM_DIALOG_ID);
       
      Bitmap bm = BitmapFactory.decodeFile(selected.getAbsolutePath());
      image.setImageBitmap(bm);
      */
     } 
    }});
        
   break;
  }
  return dialog; 
 }
  
 @Override
 protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
  // TODO Auto-generated method stub
  super.onPrepareDialog(id, dialog, bundle);
   
  switch(id) {
  case CUSTOM_DIALOG_ID:
   ListDir(curFolder);
   
   break; 
  } 
 }
  
 void ListDir(File f){
   
  if(f.equals(root)){
   buttonUp.setEnabled(false); 
  }else{
   buttonUp.setEnabled(true); 
  }
   
  curFolder = f;
  buttonSelectFolder.setText("Select Folder " + curFolder);
   
  File[] files = f.listFiles();
  fileList.clear();
  for (File file : files){
   if(file.isDirectory()){
    fileList.add(file.getPath()); 
   }else{
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     fileList.add(file.getName()); 
    } 
   } 
  }
   
  ArrayAdapter<String> directoryList
   = new ArrayAdapter<String>(this,
     android.R.layout.simple_list_item_1, fileList);
  dialog_ListView.setAdapter(directoryList); 
 }
 
 private void ListJpgInFolder(File folder){
  String jpgs = "JPG fiels in folder " + folder.getAbsolutePath() + "\n\n";
  File[] files = folder.listFiles();
  for (File file : files){
   if(!file.isDirectory()){
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     jpgs += file.getAbsolutePath() + "\n"; 
    } 
   } 
  }
  
  jpgList.setText(jpgs);
 }
  
}


main.xml, the main layout.
<?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/opendialog"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Load Folder" />
    <TextView
        android:id="@+id/jpglist"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>


Next:
- Display photos from SD in android.widget.Gallery


Jun 12, 2012

java.util.concurrent.ScheduledExecutorService

java.util.concurrent.ScheduledExecutorService is an java.util.concurrent.ExecutorService that can schedule commands to run after a given delay, or to execute periodically.

The example demonstrate how to implement ScheduledExecutorService, start a Runnable object run in background. It have to be noted that the Runnable object run in background thread, a message-handler mechanism have to be implemented to update ProgressBar in UI thread.

ScheduledExecutorService and ProgressBar


package com.AndroidScheduledExecutorService;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;

public class AndroidScheduledExecutorServiceActivity extends Activity {
 
 ProgressBar progressBar;
 Button startButton;

 int progress;
 
 ScheduledExecutorService scheduledExecutorService;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        progressBar = (ProgressBar)findViewById(R.id.progress);
        startButton = (Button)findViewById(R.id.start);
        
        startButton.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    progress = 0;
    scheduledExecutorService = Executors.newScheduledThreadPool(1);
    
    scheduledExecutorService.scheduleWithFixedDelay(
     new Runnable(){
      @Override
      public void run() {
       handler.sendMessage(handler.obtainMessage());
       }}, 
     100,
     100,
     TimeUnit.MILLISECONDS);
   }});
    }
    
    Handler handler = new Handler(){

  @Override
  public void handleMessage(Message msg) {
   progress++;
   if(progress>100){
    scheduledExecutorService.shutdown();
   }else{
    progressBar.setProgress(progress);
   }
  }
     
    };
}


<?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" />
    <ProgressBar
        android:id="@+id/progress"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_margin="20px"
  android:max="100" 
  android:progress="0"
  style="?android:attr/progressBarStyleHorizontal"/>
    <Button
        android:id="@+id/start"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
  android:text="Start ScheduledExecutorService" />

</LinearLayout>


Jun 7, 2012

Gets IP addresses of a given host - InetAddress.getAllByName()

java.net.InetAddress is a class of Internet Protocol (IP) address. This can be either an IPv4 address or an IPv6 address, and in practice you'll have an instance of either Inet4Address or Inet6Address (this class cannot be instantiated directly). Most code does not need to distinguish between the two families, and should use InetAddress.

The method getAllByName() gets all IP addresses associated with the given host identified by name or literal IP address. The IP address is resolved by the configured name service. If the host name is empty or null an UnknownHostException is thrown. If the host name is a literal IP address string an array with the corresponding single InetAddress is returned.

This example list all IP addresses associated with the user enter host name.

Gets IP addresses of a given host - InetAddress.getAllByName()


Note:

"android.permission.INTERNET" is needed.

For Applications targeting the Honeycomb SDK or higher, cannot attempts to perform a networking operation on its main thread. Otherwise, NetworkOnMainThreadException will be thrown. So the network operation is moved into AsyncTask.

I haven't IPv6 connection currently, I don't know if it will show Inet6Address with IPv6 connection. If you have, please let me know.

package com.AndroidInet;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;

public class AndroidInetActivity extends Activity {
 
 EditText hostinput;
 TextView info;
 Button btnTest;
 ListView resultList;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        hostinput = (EditText)findViewById(R.id.testhost);
        info = (TextView)findViewById(R.id.info);
        resultList = (ListView)findViewById(R.id.result);
        btnTest = (Button)findViewById(R.id.test);
        btnTest.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View view) {
    info.setText("Wait...");
    new Task().execute();
    
   }});
    }
    
    
    
    private class Task extends AsyncTask<Void, Void, Void>{
     Boolean error = false;
     String error_info = "";
     InetAddress[] inetAddress = null;
     
     List<String> hostList = new ArrayList<String>();

  @Override
  protected Void doInBackground(Void... arg0) {
   doTest();
   return null;
  }

  @Override
  protected void onPostExecute(Void result) {
   if(error){
    info.setText("Error: \n" + error_info);
   }else{
    info.setText("Finished");
    
    ArrayAdapter<String> adapter 
    = new ArrayAdapter<String>(
      AndroidInetActivity.this, 
      android.R.layout.simple_list_item_1, 
      hostList);
     
    resultList.setAdapter(adapter);
   }
   
   super.onPostExecute(result);
  }
     
  private void doTest(){
      
      String host = hostinput.getText().toString();
      
      try {
    inetAddress = InetAddress.getAllByName(host);
    
    for(int i = 0; i < inetAddress.length; i++){

     hostList.add(inetAddress[i].getClass() + " -\n"
       + inetAddress[i].getHostName() + "\n"
       + inetAddress[i].getHostAddress());
       }
    
   } catch (UnknownHostException e) {

    e.printStackTrace();
    
    error = true;
    error_info = e.toString();
   }

     }
    }
}


<?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" />
    <EditText
        android:id="@+id/testhost"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="www.worldipv6launch.org" />
    <Button
        android:id="@+id/test"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" 
        android:text="Test" />
    <TextView
        android:id="@+id/info"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <ListView
        android:id="@+id/result"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    
</LinearLayout>


Jun 4, 2012

Image Processing - inverse bitmap to generate film like picture

This article modify the last article "Image Processing with open JPG file dialog", inverse color from the original bitmap to generate film like picture.

Inverse bitmap to generate film like picture


Modify the AndroidImageProcessingActivity.java from last article "Image Processing with open JPG file dialog". Other files keep no change.

package com.AndroidImageProcessing;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.app.Dialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.MimeTypeMap;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class AndroidImageProcessingActivity extends Activity {
 
 //for Load JPG dialog
 Button buttonUp;
 static final int LOAD_DIALOG_ID = 0;
 TextView textFolder;
 ListView dialog_ListView;
 private List<String> fileList = new ArrayList<String>();
 
 File root;
 File curFolder;
 
 ImageView imageSource, imageAfter;
 Bitmap bitmap_Source;
 ProgressBar progressBar;
 
 private Handler handler;
 Bitmap afterProcess;
 
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        imageSource = (ImageView)findViewById(R.id.imageSource);
        imageAfter = (ImageView)findViewById(R.id.imageAfter);
        progressBar = (ProgressBar)findViewById(R.id.progressBar);
        progressBar.setVisibility(View.GONE);
        
        bitmap_Source = BitmapFactory.decodeResource(getResources(), R.drawable.testpicture);

        handler = new Handler();
        //performImageProcessing(bitmap_Source);
        
        root = new File(Environment
          .getExternalStorageDirectory()
          .getAbsolutePath());
        
        curFolder = root;
    }
    
    private void performImageProcessing(Bitmap bmSrc){
     imageSource.setImageBitmap(bmSrc);
     progressBar.setVisibility(View.VISIBLE);
  imageAfter.setVisibility(View.GONE);
     StratBackgroundProcess(bmSrc);
    }
    
    private void StratBackgroundProcess(final Bitmap bm_src){
     
     Runnable runnable = new Runnable(){

   @Override
   public void run() {
    //afterProcess = processingBitmap(bm_src, kernal_blur);
    afterProcess = InverseBitmap(bm_src);
    
    handler.post(new Runnable(){

     @Override
     public void run() {
      progressBar.setVisibility(View.GONE);
      imageAfter.setVisibility(View.VISIBLE);
      imageAfter.setImageBitmap(afterProcess);
     }
     
    });
   }
     };
     new Thread(runnable).start();
    }
    
    private Bitmap InverseBitmap(Bitmap src){
     Bitmap dest = Bitmap.createBitmap(
       src.getWidth(), src.getHeight(), src.getConfig());
     
     for(int i = 0; i < src.getWidth(); i++){
      for(int j = 0; j < src.getHeight(); j++){

       dest.setPixel(i, j, Color.argb(
         Color.alpha(src.getPixel(i, j)), 
         255 - Color.red(src.getPixel(i, j)), 
         255 - Color.green(src.getPixel(i, j)), 
         255 - Color.blue(src.getPixel(i, j))));
      } 
     }
     
     return dest;
    }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {
  menu.add(0, 0, 0, "Load jpg");
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  switch(item.getItemId()){
  case (0):
   showDialog(LOAD_DIALOG_ID);
   break;
  }
  return true;
 }

 @Override
 protected Dialog onCreateDialog(int id) {

  Dialog dialog = null;
  
  switch(id) {
     case LOAD_DIALOG_ID:
      dialog = new Dialog(AndroidImageProcessingActivity.this);
      dialog.setContentView(R.layout.load_dialog);
      dialog.setTitle("Load JPG");
      
      dialog.setCancelable(true);
      dialog.setCanceledOnTouchOutside(true);
      
      textFolder = (TextView)dialog.findViewById(R.id.folder);

      buttonUp = (Button)dialog.findViewById(R.id.up);
      buttonUp.setOnClickListener(new OnClickListener(){

    @Override
    public void onClick(View v) {
     // TODO Auto-generated method stub
     ListDir(curFolder.getParentFile());
    }});

      //Prepare ListView in dialog
      dialog_ListView = (ListView)dialog.findViewById(R.id.dialoglist);

      dialog_ListView.setOnItemClickListener(new OnItemClickListener(){

    @Override
    public void onItemClick(AdapterView<?> parent, View view,
      int position, long id) {
     
     File selected = new File(fileList.get(position));
     if(selected.isDirectory()){
      ListDir(selected); 
     }else {
      Toast.makeText(AndroidImageProcessingActivity.this,
        selected.toString() + " selected",
        Toast.LENGTH_LONG).show();
      dismissDialog(LOAD_DIALOG_ID);
      
      Bitmap bm = BitmapFactory.decodeFile(selected.getAbsolutePath());
      imageSource.setImageBitmap(bm);
            performImageProcessing(bm);
        }
     
    }});
      
         break;
     }

  return dialog;
 }

 @Override
 protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
  // TODO Auto-generated method stub
  super.onPrepareDialog(id, dialog, bundle);

  switch(id) {
     case LOAD_DIALOG_ID:
      ListDir(curFolder);
         break;
     }
  
 }
 
 void ListDir(File f){

  if(f.equals(root)){
   buttonUp.setEnabled(false);
  }else{
   buttonUp.setEnabled(true);
  }
  
  curFolder = f;
  textFolder.setText(f.getPath());
  
  File[] files = f.listFiles();
  fileList.clear();
  for (File file : files){
   
   if(file.isDirectory()){
    fileList.add(file.getPath());
   }else{
    Uri selectedUri = Uri.fromFile(file);
    String fileExtension
     = MimeTypeMap.getFileExtensionFromUrl(selectedUri.toString());
    if(fileExtension.equalsIgnoreCase("jpg")){
     fileList.add(file.getPath());
    }
   }
   
  }
       
  ArrayAdapter<String> directoryList
   = new ArrayAdapter<String>(this,
     android.R.layout.simple_list_item_1, fileList);
  dialog_ListView.setAdapter(directoryList);
 }

}


Read it: My "Image processing" series.


Infolinks In Text Ads