Tuesday, November 26, 2013

Image Slideshow

In this post, you will learn to create a simple image slideshow app for Android. The user can select multiple images from the external card. Then by selecting the show menu item, the images will be displayed in slideshow view. The slideshow process will stop when all images are displayed.
Now, to develop the image slideshow app, you will need to create a new Android project. The project name is ImageSlider.

The first interface that you will see when the app starts up is the list of files and folders in the root directory. From this list, you can navigate to any sub-directory that contains the images to show. You can select or deselect multiple images as you want. The text box above the list displays the current directory. Making change to path in the text box will change the contents of the list. You can use this text box to navigate back and forth in the directory structure.

image slideshow


In this app, a class called BrowseFragment that extends the Fragment class is used to display the list of files and folders from which the user can select images to show or navigate to a lower-level sub-folder. Its layout file (browse_view.xml) defines two components: one EditText and one ListView. The data source of the ListView that contains names of files and folders is set to the ListView by calling the listDirContents method of the MainActivity class. The MainActivity class is discussed later in this tutorial.  Here are the contents of the BrowserFragment.java and browse_view.xml files.

BrowseFragment.java file

package com.example.imageslider;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class BrowseFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.browse_view, container, false);
}

public static BrowseFragment newInstance(String str){

return(new BrowseFragment());
}

public void onStart(){
super.onStart();

}


}


browse_view.xml file

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/txt_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/text_hint" />
 <ListView
        android:id="@+id/files_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
         >
     
   </ListView>

</LinearLayout>


Each item of the ListView contains text and check box. The text represents a file or folder. The check box allows the user to select a file or multiple files. If a folder is selected, the files and sub-folders in that folder will be listed. The file that defines the layout of list item is called mulliststyle.xml in the res/layout directory.

mulliststyle.xml

<?xml version="1.0" encoding="utf-8"?>

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:gravity="center_vertical"
    android:checkMark="?android:attr/listChoiceIndicatorMultiple"
    android:paddingLeft="6dip"
    android:paddingRight="6dip"
/>

When the user selects the show menu item, the images will be displayed in slideshow view. You will need one more class that extends the Fragment class to display the image. This class is called ContentFragment and its layout file is content_view.xml file. The content_view.xml file simply contains the blank LinearLayout container. The component that is used to show the image will be added to the container later by code.

ContentFragment.java file

package com.example.imageslider;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class ContentFragment extends Fragment{
private static Activity mAct;
private static ArrayList<String> args;
private ArrayList<Bitmap> bitmapRes;
private LinearLayout ll;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Inflate the layout for this fragment
View v=inflater.inflate(R.layout.content_view, container, false);
ll=(LinearLayout) v.findViewById(R.id.layout_view);
//add SurfaceView representing the draw area to the layout
ll.addView(new MyDrawArea(this.getActivity()));
//Toast.makeText(this.getActivity(),"Hello="+ll.getWidth(), Toast.LENGTH_SHORT).show();
return ll;

}
public static ContentFragment newInstance(ArrayList<String> res){
args=res;
return(new ContentFragment());
}

public void onAttach(Activity activity){
super.onAttach(activity);
mAct=activity;

}

public void onStart(){
super.onStart();
//decode images
bitmapRes=processBitmap();
}

class ItemList implements OnItemClickListener{
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
        Toast.makeText(mAct.getBaseContext(), "" + position, Toast.LENGTH_SHORT).show();
        }
}

public ArrayList<Bitmap> processBitmap(){
ArrayList<Bitmap>lst=new ArrayList<Bitmap>();
//get screen dimension
DisplayMetrics metrics=new DisplayMetrics();
mAct.getWindowManager().getDefaultDisplay().getMetrics(metrics);
int rqwidth=metrics.widthPixels;
int rqheight=metrics.heightPixels;
//decode all images
for(int i=0;i<args.size();i++){
Bitmap image=decodeBitmap(args.get(i),rqwidth,rqheight);
lst.add(image);
}
return lst;
}

public Bitmap decodeBitmap(String path,int rqwidth,int rqheight){
BitmapFactory.Options option=new BitmapFactory.Options();
//specify decoding options
option.inJustDecodeBounds=true;
BitmapFactory.decodeFile(path,option);
option.inSampleSize=getSampleSize(option,rqwidth,rqheight);
option.inJustDecodeBounds=false;
return BitmapFactory.decodeFile(path,option);
}

public int getSampleSize(BitmapFactory.Options option, int rqwidth,int rqheight){
int samplesize=1;
int width=option.outWidth;
int height=option.outHeight;
if(width>rqwidth || height>rqheight){
int widthradio=Math.round((float)width/(float)rqwidth);
int heightradio=Math.round((float)height/(float)rqheight);
samplesize=widthradio<heightradio? widthradio:heightradio;

}
return samplesize;
}
//An image can be drawn on SurfaceView
class MyDrawArea extends SurfaceView implements Callback{
private Bitmap bitImage;
Paint p;
MyDrawArea(Context context){
super(context);
getHolder().addCallback(this);
getHolder().setFormat(PixelFormat.TRANSPARENT);
p=new Paint();


}
//This method will be called from the run method to show the image
public void drawImage(){

   Canvas canvas = this.getHolder().lockCanvas();
   canvas.drawColor(Color.BLACK);
   canvas.drawBitmap(bitImage, 0, 0, p);
   this.getHolder().unlockCanvasAndPost(canvas);
}
public void surfaceCreated(SurfaceHolder holder){
SlideThread thread=new SlideThread(holder,this);
thread.start(); //start images slide show
requestLayout();
}
public void surfaceDestroyed(SurfaceHolder holder){

}
public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){

}

public void setBitmap(Bitmap bitmap){
bitImage=bitmap;
}
}

class SlideThread extends Thread{
MyDrawArea marea;
SlideThread(SurfaceHolder holder,MyDrawArea area){
marea=area;
}

public void run(){
for(int i=0;i<bitmapRes.size();i++){
try{
marea.setBitmap(bitmapRes.get(i)); //set the image to show on the drawing area
marea.drawImage(); //call the drawImage method to show the image
Thread.sleep(2200); //delay each image show
}catch(InterruptedException ie){}

}
}

}
}


content_view.xml file

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="horizontal"
/>



In the ContentFragment class, the drawing area object (MyDrawArea class extended SurfaceView class) is added to layout so that the images can be displayed. The SurfaceView class has a method called surfaceCreated. In this method, you will write code to show an images in slideshow view. The SlideThread extends the Thread class handles the image slideshow process. This thread will start from the surfaceCreated method. The run method of the SlideThread class is called when the thread starts. It loops throught the ArrayList object that contains the decoded images. The delay time between images show is specified by the sleep method of the Thread class. While the loop is working the drawImage method is called to show the image on the drawing area.
The ArrayList bitmapRes is used to store the decoded images. The processImage method is called when the ContentFragment starts. The processImage method will decode the images and add them to the bitmapRes. When decoding the image, you can specify a single dimension for all images to fit the screen. It is not good to display an 1050 x 1000 image on the 240 x 432 screen.

The MainActivity class that extends the FragmentActivity class will be used as the container of the BrowseFragment and ContentFragment. When the app firstly starts, BrowseFragment is added to container to display list of files and folders. The BrowseFragment is replaced by the ContentFragment when the user touches the show menu item to display the images. Below are the content of the MainActivity.java file and the layout file of the MainActivity class.

MainActivity.java file

package com.example.imageslider;
import java.io.File;
import java.util.ArrayList;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentTransaction;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends FragmentActivity {
ArrayList<String> lstcheckeditems;
int mindex=0;
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState != null) {
        return;
        }
        //display the browse fragment to show the list of files and folders
        BrowseFragment bf=new BrowseFragment();
        FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
        transact.add(R.id.fragment_container,bf);
        transact.addToBackStack(null);
        transact.commit();
     
 
    }

public void onBackPressed() {
LinearLayout view = (LinearLayout) findViewById(R.id.layout_view);      
if(view!=null){
BrowseFragment df=new  BrowseFragment();
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.replace(R.id.fragment_container, df);
transact.addToBackStack(null);
transact.commit();
onStart();
}
else
System.exit(0);
}

public void onStart(){
 super.onStart();
 regControls();
 lstcheckeditems=new  ArrayList<String>() ;
}

public void regControls(){
EditText et=(EditText) findViewById(R.id.txt_input);
    et.addTextChangedListener(new ChangeListener());
    et.setText("/");
    ListView lv=(ListView) findViewById(R.id.files_list);
    lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    lv.setOnItemClickListener(new ClickListener());
}

class ChangeListener implements TextWatcher{
   
    public void beforeTextChanged(CharSequence s, int start, int before, int count){
   
   
    }
   
    public void onTextChanged(CharSequence s, int start, int before, int count){
    EditText et=(EditText) findViewById(R.id.txt_input);
    String txt=et.getText().toString();
    listDirContents(txt);
    }
   
    public void afterTextChanged(Editable ed){
   
   
    }
    }
 
    class ClickListener implements OnItemClickListener{
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      // selected item
            String selectedItem = ((TextView) view).getText().toString();
            EditText et=(EditText) findViewById(R.id.txt_input);        
            String path=et.getText().toString()+"/"+selectedItem;          
            File f=new File(path);
            if(f.isDirectory())
            et.setText(path);            
         
            else if(f.isFile() && (path.endsWith(".jpg") || path.endsWith(".png") || path.endsWith(".gif")))
            {
            if(lstcheckeditems.contains(path))
            lstcheckeditems.remove(path);
            else
            lstcheckeditems.add(path);
            }
           
      }
      public void onNothingSelected(AdapterView<?> parent){
     
      }
     
     
      }
 

 
    public void listDirContents(String path){
    ListView l=(ListView) findViewById(R.id.files_list);
   
    if(path!=null){
    try{
    File f=new File(path);    
    if(f!=null){
    String[] contents=f.list();
    if(contents.length>0){
    ArrayAdapter<String> aa=new ArrayAdapter<String>(this,R.layout.muliststyle,contents);
    l.setAdapter(aa);
    }
    }
    }catch(Exception e){}
    }
   
 
   
    }

    public void show(ArrayList<String> res){
    if(res.size()>0){
    ContentFragment cf=ContentFragment.newInstance(res);
    FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
    transact.replace(R.id.fragment_container, cf);
    transact.addToBackStack(null);
    transact.commit();
    }
   
    }
 
 
 
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
    public boolean onOptionsItemSelected(MenuItem item)
    {
     
        switch (item.getItemId())
        {
        case R.id.menu_show:
        show(lstcheckeditems);
             return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }  

 
}

activity_main.xml file

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

The regControls method registers the text change event to the EditText component and the click event to the ListView component so that the user can make change to the text box and select items of the list. It is called when the main activity starts.
The listDirContents is also called when the main activity starts to read the files and folders from the root directory of Android device and show them in the list.
The show method is called when the user touches the show menu item. This method will replace the BrowseFragment by the ContentFragment fragment on the MainActivity. Then the images slideshow works. You will need to edit the main.xml file in the res/menu directory. This file defines the show menu item. The content of the main.xml file should look similar to the following:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:id="@+id/menu_show"
     android:title="Show" />
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_settings"/>
 

</menu>

Now you are ready to run the program and test it. If you have questions, please leave them at the comment section. I will reply as soon as possible.

Thursday, November 14, 2013

Browser History and bookmarks

In this post, you will learn to create a simple Android app to view browser history and bookmarks. This app displays the browser history and bookmarks in a single list. It can also be used to clear the browser history from your Android device. In addition, the user is able to click a link on the list to open the page on the browser.
To develop this app, you will need to create an Android project in Eclipse. The name of the project or application will be BClean. The BClean app has the simple user interface. When the app firstly launches, it displays a list of browser history and bookmarks. You will need to edit the activity_main.xml file to include a ListView component. Here is the content of the activity_main.xml file.

activity_main.xml file

<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"
    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=".MainActivity" >

    <ListView
     
        android:id="@+id/hb_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
     
    </ListView>

</RelativeLayout>


Each item of the list will display three fields of each browser history or bookmark. The first field is the icon. The second field is the title and the third field is the url.

browser history and bookmarks view and clean


To customize the ListView component to allow each row or item to show the data in that way, you will need the listlayout.xml and ListAdapterModel.java files. The listyout.xml file represents the layout of each row or item of the list. There are three components in the listlayout file. The first component is an ImageView. It is for displaying the icon. The second and third components are TextView compoennts for displaying the title and url.

listlayout.xml file

<?xml version="1.0" encoding="utf-8"?>
<!--  Single List Item Design -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="5dip" >
<ImageView
    android:id="@+id/icon"
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:padding="5sp"
    android:contentDescription="icon_image"
 />
<LinearLayout
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:orientation="vertical"
    >

<TextView
       android:id="@+id/hbtitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="1sp"
        android:textSize="20sp"
        android:scrollHorizontally="true"
    android:singleLine="true"
        android:textStyle="bold" >
</TextView>
<TextView
       android:id="@+id/hburl"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="1sp"
        android:textSize="15sp"
        android:scrollHorizontally="true"
    android:singleLine="true"
        android:textColor="#0000ff">
 </TextView>
</LinearLayout>
</LinearLayout>

The ListAdapterModel.java file defines a class called ListAdapterModel that extends ArrayAdapter class. The object of the ListAdapterModel class will be used as the data source of the list. Below is the content of the ListAdapterModel. java file.

ListAdapterModel. java file

package com.example.bclean;

import java.util.ArrayList;
import android.content.Context;
import android.graphics.Bitmap;
import android.text.util.Linkify;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;


public class ListAdapterModel extends ArrayAdapter<String>{
int groupid;
ArrayList<String> titles;
ArrayList<String> urls;
ArrayList<Bitmap> bitmaps;
Context context;
String path;
public ListAdapterModel(Context context, int vg, int id, ArrayList<String> titles,ArrayList<String> urls,ArrayList<Bitmap> bitmaps){
super(context,vg, id, titles);
this.context=context;
groupid=vg;
this.titles=titles;
this.urls=urls;
this.bitmaps=bitmaps;

}
public View getView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        View itemView = inflater.inflate(groupid, parent, false);
        ImageView imageView = (ImageView) itemView.findViewById(R.id.icon);
        imageView.setImageBitmap(bitmaps.get(position));
        TextView textTitle= (TextView) itemView.findViewById(R.id.hbtitle);
        String title=titles.get(position);
        textTitle.setText(title);
        TextView textURL = (TextView) itemView.findViewById(R.id.hburl);
        String url=urls.get(position);
        textURL.setText(url);
        //make the url clickable
        Linkify.addLinks(textURL, Linkify.ALL);
        return itemView;
}


}

The most important file in this application is MainActivity.java file. This file contains code that reads, display  the browser history and bookmarks, and clean the history data.
In the onCreate method of the MainActivity class, three ArrayList objects are created by calling the createLists method. One ArrayList object is to store the titles of the browser history and bookmarks. One is for storing the urls. And the last one will store the icons. The three ArrayList objects will be supplied to the ListAdapterModel class to show the browser history and bookmarks on the list.

MainActivity.java file

package com.example.bclean;
import java.util.ArrayList;
import android.os.Bundle;
import android.provider.Browser;
import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.ListView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private ArrayList<String> titles;
    private ArrayList<String> urls;
    private ArrayList<Bitmap> bitmaps;
    private ContentResolver cr;
     protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        createLists();
       }
    protected void onResume(){
    super.onResume();
    getBH();      
    showHistoryBookmarks();
   
    }
 
    public void createLists(){
    titles=new ArrayList<String>();
    urls=new ArrayList<String>();
    bitmaps=new ArrayList<Bitmap>();
 
    }
    public void getBH(){
    Bitmap icon;
    cr=getContentResolver();
    String order=Browser.BookmarkColumns.DATE+" DESC";
    String[] projection={Browser.BookmarkColumns.TITLE,Browser.BookmarkColumns.URL,Browser.BookmarkColumns.FAVICON};
    //String selection=projection[0]+"=?";
    //String args[]={"Google"};
    Cursor rows=cr.query(Browser.BOOKMARKS_URI,projection, null,null,order);
    if(rows.getCount()>0){
    while(rows.moveToNext()) {
    //read title
    String title=rows.getString(rows.getColumnIndex(projection[0]));
    //read url
    String url=rows.getString(rows.getColumnIndex(projection[1]));
    //read icon
    byte[] bicon=rows.getBlob(rows.getColumnIndex(projection[2]));
    if(bicon!=null){
    //convert blob image data to Bitmap
    icon=BitmapFactory.decodeByteArray(bicon,0,bicon.length);  
   
    }
   
    else{
    //default icon for history and bookmarks that do not icons
    icon=BitmapFactory.decodeResource(getResources(),R.drawable.noicon);
    }
    //add to lists
    addToList(title,url,icon);
    }
    //close the cursor
    rows.close();
    }    
   
   
    }  
 
 
    public void addToList(String title,String url, Bitmap bitmap){
   
    titles.add(title);
    urls.add(url);
    bitmaps.add(bitmap);
   
    }

 
    public void showHistoryBookmarks(){
    ListView l=(ListView) findViewById(R.id.hb_list);
if(l!=null){
if(titles.size()>0){
ListAdapterModel aa=new ListAdapterModel(this,R.layout.listlayout,R.id.hbtitle,titles,urls,bitmaps);  
l.setAdapter(aa);
}
else{
Toast.makeText(this, "This is no bookmark or history.", Toast.LENGTH_SHORT).show();
}
}

}
 
    public void cleanHB(){
    if(Browser.canClearHistory(cr)){
    Browser.clearHistory(cr); //clear history data
    createLists(); //recreate the lists
    onResume(); //update the list
    }
   
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
    public boolean onOptionsItemSelected(MenuItem item){
    if(item.getItemId()==R.id.action_clean)
    {
    cleanHB();
   
    }
    return false;
    }

 
}

The getHB method reads the browser history and bookmarks from Android and add the data (titles, urls, and icons) on the list. To get the browser history and bookmarks from Android, you will use the ContentResolver class. The instance of this class can be obtained by invoking the getContentResolver method from the current activity. By using the query method of the ContentResolver class, you can read all browser history and bookmarks. These history and bookmarks are stored in a Cursor object. The query method has five arguments. The first argument accepts the url that the data will be read from. This app reads the browser history and bookmarks. Thus, the url is BOOKMARKS_URI from the Browser class. The second argument allows you to specifiy an array of fields or column names to be retrieved. In this app, their are only three fields that will be retrieved TITLE, URL, and FAVICON. You will need to use the BookmarkColumns class in Browser class to access the field names of the table that stores the browser history and bookmarks. The third and forth arguments specifies the selection (field and operator) and selection arguments (values) to retrieve the rows of the table. Since the app retrieves all rows, you will use null values for these arguments. The last argument of the query method allows you to specify the field that the rows will be ordered by.
The title, and urls are text (string). The icon field is of Blob type. The data in Blob type can be converted to the Bitmap by using the decodeArrayByte method of the BitmapFactory class.
The showHistoryBookmarks will be called after the getHB method to display the titles, urls, and icons of the browser history and bookmarks on the list. In this method, the ListAdapterModel object is created and the titles, urls, and icons are passed to it.
The cleanHB method is called when the user touches the clean menu item. It will remove all history data from Android. The list will be updated to reflect the change by calling the onResume method. You will need to edit the main.xml file (in menu folder) to add the clean item to the menu. Here is the content of the main.xml file.
main.xml file

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/action_clean"
        android:orderInCategory="100"
        android:showAsAction="never"
        android:title="@string/action_clean"/>


</menu>

Before running the BClean app, you also need to edit the AndroidManifest.xml file to allow the application to read and write browser history and bookmarks. Please add the following permissions to the file.

<uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
<uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS" />


Download the apk file of the BClean app

Friday, November 8, 2013

Keyboard

In this post, you will learn to set up a custom keyboard in your Android app. Implementing the custom keyboard can be useful when your app has to work with a language rather than English.
For this tutorial, i will talk about setting up a custom keyboard that has only twelve buttons. The nine-digit buttons are labeled from 0 to 9. The remaining two buttons are the delete button (represented by the delete icon) and the dot button. When a digit button is pressed, its label will be appended to the EditText component. The delete button will remove the last character from the EditText. The dot button allows the user to append a dot sign (.) to the EditText.

 keyboard


To follow this tutorial, now you need to create a new Android project in Eclipse. The project name will be Keyboard.
The first step you will do in setting up the custom keyboard is adding the KeyboardView component in the activity_main.xml file. We also need an EditText component to display the characters pressed by the user. Here is the content of the activity_main.xml file.
activity_main.xml file

<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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <EditText
        android:id="@+id/txt_edit"
        android:layout_width="wrap_content"
        android:layout_height="0dip"
        android:layout_weight="1"
        android:gravity="top"
         />

    <android.inputmethodservice.KeyboardView
        android:visibility="gone"
        android:id="@+id/customkeyboard"
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        android:layout_gravity="bottom"
     
   />


</LinearLayout>


For another step, you have to write the buttons of the keyboard in a layout xml file. In this tutorial, this layout xml file of the keyboard is called keyboard.xml. The buttons can be grouped in rows by using the row tags. Each row consists of four to five buttons. You need to specify the code of each button and its label or icon. All row tags will be placed in the Keyboard tag. Below is the content of the keyboard.xml file.

keyboard.xml file

<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
    android:keyWidth="33%p" android:horizontalGap="0px"
    android:verticalGap="0px" android:keyHeight="54dip">
     <Row>
             
                <Key android:codes="49" android:keyLabel="1" />
                <Key android:codes="50" android:keyLabel="2" />              
          <Key android:codes="51" android:keyLabel="3"/>
          <Key android:codes="8"  android:keyIcon="@drawable/delete_icon" />
        </Row>

        <Row>
             
                <Key android:codes="52" android:keyLabel="4" />
                <Key android:codes="53" android:keyLabel="5" />
                <Key android:codes="54" android:keyLabel="6" />
                <Key android:codes="55" android:keyLabel="7" />
        </Row>
        <Row>
             
                <Key android:codes="56" android:keyLabel="8" />
                <Key android:codes="57" android:keyLabel="9" />
                <Key android:codes="48" android:keyLabel="0" />
                <Key android:codes="46" android:keyLabel="."/>
        </Row>

</Keyboard>


In the keyboard.xml file, the delete button is presented by the delete icon. Instead of using the keyLabel to specifying label of the button, you will use the keyIcon to specify the icon of the delete button.

In the last step, you need to write code to place the keyboard layout on the KeyboardView component and show it and to receive keys pressed by the user. The code will be written in the MainAcivity.java file. The Keyboard object will be created to point to the layout file. Then this object is supplied to KeyboardView component so that it is ready to show.
To receive the keys pressed on the keyboard, the KeyboardView component must be registered with the KeyboardActionListenter interface. The onPress method of the interface has to be implemented to receive the keys. Here is the content of the MainActivity.java file.

MainActivity.java file

package com.example.keyboard;


import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
import android.os.Bundle;
import android.app.Activity;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends Activity{

    private EditText et;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //create Keyboard object
        Keyboard keyboard=new Keyboard(this, R.layout.keyboard);
        //create KeyboardView object
        KeyboardView keyview=(KeyboardView)findViewById(R.id.customkeyboard);
        //attache the keyboard object to the KeyboardView object
        keyview.setKeyboard(keyboard);
        //show the keyboard
        keyview.setVisibility(KeyboardView.VISIBLE);
        //take the keyboard to the front
        keyview.bringToFront();
        //register the keyboard to receive the key pressed
        keyview.setOnKeyboardActionListener(new KeyList());
        et=(EditText)findViewById(R.id.txt_edit);
     
    }
    class KeyList implements OnKeyboardActionListener{
    public void onKey(View v, int keyCode, KeyEvent event) {
   
    }
      public void onText(CharSequence text){
   
    }
    public void swipeLeft(){
   
    }
    public void onKey(int primaryCode, int[] keyCodes) {
   
    }
    public void swipeUp(){
   
    }
    public void swipeDown() {
   
    }
    public void swipeRight() {
   
    }
    public void onPress(int primaryCode) {
   
    if(primaryCode==8){ //take the last character out when delete button is pressed.
    String text=et.getText().toString();
    if(et.length()>0){
    text=text.substring(0,text.length()-1);
    et.setText(text);
    et.setSelection(text.length());
    }
    }
    else{
    char ch=(char)primaryCode;
    et.append(""+ch);
    }
    }
    public void onRelease(int primaryCode) {
   
    }
    }
 

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
}

Now you are ready to run the Keyboard app. If you have any questions, please leave them at the comment section. I will reply as soon as possible.

Saturday, November 2, 2013

Google Map

In this tutorial, you will learn to create a Google Map app. The Google Map app will display the Google map and spot the current location of the device on the map by a red-filled circle. In case that the current location can not be retrieved at the time, you will not see the red-filled circle at the correct location. When the user touches any location on the map, the address of that location will be shown. The address to display will include latitude, longitude, street, city, and country. Without internet connection, the app is able to show only the latitude and longitude of the location. To get the full address as mentioned, you need to make sure the internet connection work properly.



To begin the GoogleMap app, you will create a new Android Project in Eclipse. The name of the project will be GMap.

Getting Google Map to work in your app requires many steps as shown below.

1. Download and install Google Play Service API. You can use the Android SDK Manager to install this API. You would get the google-play-service.jar file stored in the directory where you store the Android SDK. In my machine, this is the path of the jar file: D:\androidbundle\sdk\extras\google\google_play_services\libproject\google-play-services_lib\libs. To use the API in the GMap project, you need to add this jar file into the project build path (Project->Properties->Java Build Path->Libraries->Add External Jars...) of the Eclipse.

2. Get the SHA-1 fingerprint for your certificate. For Window 7 or Vista users, you can get the SHA-1 key by issuing the following command from the command prompt window. You will need to change the path of the keystore file. In my case, the keystore file is in the path C:\Users\Acer\.android.

keytool -list -v -alias androiddebugkey -keystore C:\Users\Acer\.android\debug.keystore -alias name: androiddebugkey -storepass android -keypass android

3. You would see the output similar to this:
Creation date: Sep 8, 2013
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Android Debug, O=Android, C=US
Issuer: CN=Android Debug, O=Android, C=US
Serial number: 5883f7cc
Valid from: Sun Sep 08 14:28:53 ICT 2013 until: Tue Sep 01 14:28:53 ICT 2043
Certificate fingerprints:
MD5: 2A:9E:7C:7B:87:6C:5D:1F:B4:84:C0:84:BB:45:10:69
SHA1: D0:91:62:F8:32:45:B1:89:94:B3:79:B4:FD:DD:64:22:C9:72:B9:E3
SHA256: FF:4C:BF:52:EA:C1:0E:0D:FF:C0:8E:C3:2A:0D:41:ED:07:DA:3A:3D:40:
81:7F:2C:9A:13:17:AD:F5:C6:78:5A
Signature algorithm name: SHA256withRSA
Version: 3
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 2F 69 DC 79 F8 A5 07 1A 10 61 EC BD 1D E0 58 AC /i.y.....a....X.
0010: 94 CB 18 C5 ....
]
]

4. Obtain API key from Google. To get the API key from Google, you can follow the steps below:
- Open Google Console then create a new project by clicking the Create... from the drop-down list.
- Select API Access from the active project you created. In the resulting page, click Create New Android Key....In the resulting page, you are required to enter the SHA-1 key, semi-colon, and the package name of the your project. See the picture below.



- You will get the API key similar to this AIzaSyB6aqPG9XhbPMGVzkoohdlgK2HzRHe85nA.
- Turn on Goole Map Android API v2 service. You will select Services and turn on Goole Map Android API v2.
- Register the API key to your GMap app. You will open the AndroidManifest.xml file of the app and just above </application> paste the following code.

<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="AIzaSyB6aqPG9XhbPMGVzkoohdlgK2HzRHe85nA"/>
- You will need to set some permissons and the use of OpenGL ES version 2 feature in the AndroidManifest file of your app.
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<!-- The following two permissions are not required to use
Google Maps Android API v2, but are recommended. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />

This is the complete AndroidManifest.xml file.

AndroidManifest.xml file

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.gmap"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
 
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

  <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission     android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<!-- The following two permissions are not required to use
     Google Maps Android API v2, but are recommended. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/gmaptr"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.gmap.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>
    <meta-data
    android:name="com.google.android.maps.v2.API_KEY"
    android:value="AIzaSyB6aqPG9XhbPMGVzkoohdlgK2HzRHe85nA"/>
    </application>

</manifest>


Now you are ready to add a map to the GMap app. You will copy and paste (override) the following code to the activity_main.xml file.

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.google.android.gms.maps.SupportMapFragment"
/>

In the MainActivity class, you will need more code to show the map, apply the settings to map, identify the current location, and display the address of the location when the user touches that location. Here is the content of the MainActivity class.

MainActivity. java file

package com.example.gmap;
import java.util.List;
import java.util.Locale;
import android.content.Context;
import android.graphics.Color;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.Menu;
import android.widget.Toast;

import com.google.android.gms.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMapClickListener;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;

public class MainActivity extends FragmentActivity{
  private GoogleMap map;
  private LocationManager locationManager;
  private Location mylocation;
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    determineLocation();
  }

  protected void onStart() {
       super.onStart();
       setupMap();
       appMapSettings();
       spotCurrentLocation(mylocation);
  }

  public void setupMap(){
 if(map==null){
  SupportMapFragment mf=(SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);  
   map=mf.getMap();
     
 }

  }
 

  public void appMapSettings(){
 if(map!=null){
 //enable map click
 map.setOnMapClickListener(new MapClick());
 //specify the type of map
 map.setMapType(GoogleMap.MAP_TYPE_HYBRID);
 //enable my location on the map
 map.setMyLocationEnabled(true);
 //enable button for my location
 UiSettings uis=map.getUiSettings();
 uis.setMyLocationButtonEnabled(true);



 }
  }

  public void spotCurrentLocation(Location location){
     double lat,lng;
   
// Instantiates a new CircleOptions object and defines the center and radius
 CircleOptions circleOptions = new CircleOptions();
 circleOptions.strokeColor(Color.RED);
 circleOptions.fillColor(Color.RED);
 if(location==null) {//default center
 lat=12.7333;
   lng=105.6666;
 }

 else{
 lat=location.getLatitude();
 lng=location.getLongitude();
 }
 //center based on the current location
     circleOptions.center(new LatLng(lat,lng));
 circleOptions.radius(1000); // In meters
 Circle circle = map.addCircle(circleOptions);
 circle.setVisible(true);
 //set target location
 CameraUpdate center=CameraUpdateFactory.newLatLng(new LatLng(lat,lng));
         CameraUpdate zoom=CameraUpdateFactory.zoomTo(15);
         //set zoom level
     map.moveCamera(center);
     map.animateCamera(zoom);
  }
  class MapClick implements OnMapClickListener{

 public void onMapClick(LatLng coor){

 doInBackground(coor);

}

  }

  public void doInBackground(LatLng coordinate){
 final LatLng coor=coordinate;
 Handler handler=new Handler();
 handler.post(new Runnable(){
 public void run(){
 showAddress(coor.latitude,coor.longitude);
 }
 });
  }

  public void showAddress(double lat,double lng){

 Geocoder geocoder =new Geocoder(getBaseContext(), Locale.getDefault());
 List<Address> addresses = null;
 String addressText="";
 int count=0;
 try {    
 addresses = geocoder.getFromLocation(lat, lng, 1);
 while(count<10){
 addresses = geocoder.getFromLocation(lat, lng, 1);
 count++;
 }
 } catch (Exception e1) {Log.e(this.toString(),"Error...");}

 if (addresses != null && addresses.size() > 0) {
// Get the first address
Address address = addresses.get(0);
//get street, city, and country
addressText =address.getMaxAddressLineIndex()>0?address.getAddressLine(0)+", ":"null, ";
addressText+=address.getLocality()+", "+address.getCountryName();

 }

 Toast.makeText(getBaseContext(), "Address:("+lat+","+lng+") "+addressText, Toast.LENGTH_SHORT).show();
  }



  public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
  }

  public void determineLocation() {
String location_context = Context.LOCATION_SERVICE;
//Create locationManager object from the Android system location service
locationManager = (LocationManager)getSystemService(location_context);
//retrieve the available location providers
  List<String> providers = locationManager.getProviders(true);
  for (String provider : providers) {
 
  locationManager.requestLocationUpdates(provider, 1000, 0, new LocationListener() {
  public void onLocationChanged(Location location) {
  //spot the current update location
  spotCurrentLocation(location);
  }
  public void onProviderDisabled(String provider){}
  public void onProviderEnabled(String provider){}
  public void onStatusChanged(String provider, int status, Bundle extras){}
  });
  //get the current device' location from the provider
  mylocation= locationManager.getLastKnownLocation(provider);
 
  }
 
  }

}

In the onCreate method, the determineLocation method is called to detect the current location of the device. The getSystemService method returns the LocationManager object. This object will be used to get the information about location providers. Each provider represents a different technology used to determine the current locaiton. A location on the device changes when the device moves. So to get the current updated location from Android, you will need to invoke the requestLocationUpdates method of the LocationManager class. The getLastKnownLocation method will be used to get the current locaiton of the device.

In the onStart method, the setupMap, appMapSettings, and spotCurrentLocation methods are invoked. The setupMap method will show the map. The appMapSettings method specifies settings for the map. You will read he comments in code to get the idea about each setting. The spotCurrentLocation will spot the current location by a red-filled circle. The circle will cover 1000 meters around the current location.

The showAddress method will be called each time the user touched the map. The getFromLocation method of the Geocoder class is used to get the address of the current location. This method return a string that contain street, city, and country of the current location.

Download the apk file of the GMap app.