Pages

Friday, October 11, 2013

Uploader for Android

In this post, you will learn to create an uploader for Android. The uploader app can be used to connect to an FTP server and transfer files between the client and the server. For someone who doesn't want to learn how to create the file uploader, but wants to download and install this app on your devices, you can jump to the bottom of the page. You will find the download link there. It is free.
The API that is used in this app is commons-net. In this API, the FTPClient class is used to connect, and upload files to the server, and download files from the server. You can download this API from Apache website.

Now open the Eclipse and create a new Android Project called AndFTP. You need to extract the package of the commons-net API and copy and paste the commons-net-3.3.jar to the libs directory of your Android project.

For this uploader for Android, you need to create two Fragments. The first fragment is called LoginFragment. This fragment represents a login form. On the log in in form, the user will fill in the ftp domain name (e.g. ftp.worldbestlearningcenter.com), the username, and the password to connect to the ftp server. The Connect button will connect to the ftp server when all required information are filled.

Uploader for Android-Transfering file interface

LoginFragment.java file

package com.example.andftp;

//import com.actionbarsherlock.app.SherlockFragment;

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

public static LoginFragment newInstance(String str){

return(new LoginFragment());
}


}


The layout file that is the resource for the LoginFragment is called login_fragment.xml file. Its content is shown below.

login_fragment.xml file

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

      <EditText
        android:id="@+id/txt_ftpserver"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/txt_ftpserver"
        android:inputType="text" />

  <EditText
        android:id="@+id/txt_username"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/txt_username"
        android:inputType="text" />

    <EditText
        android:id="@+id/txt_password"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/txt_password"
        android:inputType="textPassword" />


     <Button
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:onClick="connectToServer"
         android:text="@string/bt_connect"

          />

</LinearLayout>


When you are successfully connect to the server, the second fragment (TransferFragment) will display. On this user interface, the user can select a file from the list of local files and push the upload icon (the arrow with the right-direction head) to upload or transfer the selected file to the server. The right-side list displays the files and directories of the remote server. From the right-side list, you can select a file or navigate to any directory that you want. The selected file can be downloaded from the sever to your device by pressing the download icon ( the arrow with the left-direction head). Above each list (local and server), there is a text box to display the currently selected file or directory. It also allows you to enter the path of your desired file or directory.

TransferFragment.java file

package com.example.andftp;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class TransferFragment extends Fragment{
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment

return inflater.inflate(R.layout.transfer_fragment, container, false);
}
public static TransferFragment newInstance(){
return(new TransferFragment());
}


}




The layout file of the TransferFragment can be written as shown below.

transfer_fragment.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:orientation="horizontal"
>
<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <EditText
        android:id="@+id/txt_local"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:hint="@string/txt_local" />
 
    <ListView
        android:id="@+id/localfiles_list"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
   
        />
    </LinearLayout>
    <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_gravity="center"
    >
        <ImageButton
android:id="@+id/bt_upload"
          android:src="@drawable/upload_icon"        
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:onClick="uploadFile"
          android:background="#ffffff"
          android:layout_marginBottom="30dp"
       />
         <ImageButton
android:id="@+id/bt_download"
          android:src="@drawable/download_icon"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:onClick="downloadFile"
          android:background="#ffffff"
          android:layout_marginTop="30dp"
     />
</LinearLayout>
<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
 <EditText
        android:id="@+id/txt_server"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:hint="@string/txt_server"
     
         />
<ListView
        android:id="@+id/serverfiles_list"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
   
        />
</LinearLayout>

</LinearLayout>


As you see in the screenshot above, each list (either local or server list) has two parts--icon and text. The icon displays the file icon or directory icon. The text part displays the file or directory name. The two lists use the same layout file called listlayout.xml. This is the content of the listlayout.xml file.

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"
    />

<TextView
    android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#0000ff"
        android:textStyle="bold" >
</TextView>
</LinearLayout>


The ListAdapterModel class that will be used as the data source of each list is written in the ListAdapterModel.java file as shown below.

ListAdapterModel.java file

package com.example.andftp;

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

//import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;

import android.content.Context;
//import android.util.Log;
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> filenames;
Context context;
String parentpath;
ArrayList<FTPFile> ftpfiles;
public ListAdapterModel(Context context, int vg, int id,ArrayList<String> filenames, String parentPath,ArrayList<FTPFile> ftpfiles){
super(context,vg, filenames);
this.context=context;
groupid=vg;
this.filenames=filenames;
this.parentpath=parentPath;
this.ftpfiles=ftpfiles;
}
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);
        TextView textView = (TextView) itemView.findViewById(R.id.label);
        String item=filenames.get(position);
        textView.setText(item);
   
        if(ftpfiles==null){ //Set text and icons to local files and directories
          File f=new File(parentpath+"/"+item);
        if(f.isDirectory())
        imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.diricon));
        else
        imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.fileicon));
        }
        else{ ////Set text and icons to files and directories retrieved from server
       
        if(ftpfiles.get(position).isDirectory())
          imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.diricon));
        else{
          imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.fileicon));
         
        }
        }
     
        return itemView;
}

}


To learn more about customizing the ListView, you will read the previous post  FileChooser, Send Email, or Currency Converter.

Normally, when the Fragment class is used to construct the sub-user interface. The main activity that represents the main interface must be created by using the FragmentActivity class. The layout file (activity_main.xml file) simply contains a FrameLayout view that will be the container of other sub-interface fragments.

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"

/>

Now we take a look at the MainActivity.java file. This file is the main part of the uploader for Android app. It contains more than 600 lines of code.

MainActivity.java file

package com.example.andftp;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
//import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
//import android.app.Activity;
import android.content.pm.ActivityInfo;
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.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
//import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends FragmentActivity {

    private String localpath;
    private String serverpath;
    private FTPClient client;
     private ProgressDialog pd;
    private Context context;
    private ArrayList<FTPFile> ftpobjects;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        client= new FTPClient();    
    context=this;
        //prevent the LoginFragment from being recreated when the orientation changes
        if(findViewById(R.id.fragment_container)!=null){
        if (savedInstanceState != null) {
        return;
        }
        }
        LoginFragment lf=new LoginFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,lf).commit();
     
     
    }
    public void initLogin(){
    String[] loginInfo=readLogin();
if(loginInfo!=null){
EditText txtserver=(EditText) findViewById(R.id.txt_ftpserver);
EditText txtusername=(EditText) findViewById(R.id.txt_username);
EditText txtpassword=(EditText) findViewById(R.id.txt_password);
txtserver.setText(loginInfo[0]);
txtusername.setText(loginInfo[1]);
txtpassword.setText(loginInfo[2]);

}
    }
 
    protected void onStart(){
    super.onStart();
    //default local path
    localpath=Environment.getExternalStorageDirectory().toString();
    //default server path
    serverpath="/www";
      //read existing login information
    initLogin();
   
    }

    protected void onResume(){
    super.onResume();
    regControls();
   
    }
 
    protected void onDestroy(){
    super.onDestroy();
    //filenames=client.listNames();
   
    if(pd!=null){
    pd.dismiss();
    }
    if(client.isConnected()){
   
    try {
    client.logout();
client.disconnect();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    }
   
    }
    @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 void connectToServer(View view){
    boolean connected=connect();
    if(connected){
   
    showTransferUI();
    }
    else
    Toast.makeText(context,"Unable to connect to the server", Toast.LENGTH_SHORT).show();
    }
 
    public boolean connect(){
    boolean connected=false;
    EditText txtserver=(EditText) findViewById(R.id.txt_ftpserver);
EditText txtusername=(EditText) findViewById(R.id.txt_username);
EditText txtpassword=(EditText) findViewById(R.id.txt_password);
String servername=txtserver.getText().toString();
String username=txtusername.getText().toString();
String password=txtpassword.getText().toString();
if(servername.length()<=0 || username.length()<=0 || password.length()<=0){

    AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage("Please fill all text boxes");
        builder.setCancelable(true);
        AlertDialog dialog = builder.create();      
        dialog.show();
     
}

else{
//save login info
saveLogin(servername,username,password);
try {

//disconnect the previous connection
if(client.isConnected()){
client.logout();
client.disconnect();
}
//connect to the remove ftp server
client.connect(servername);
//log in to the server with user name and password
   client.login(username, password);
   //set the passive mode for the file transfer
   client.enterLocalPassiveMode();
   //upload file to this directory
   //client.changeWorkingDirectory("/www");
   System.out.println(client.getReplyString());
   int reply=client.getReplyCode();
    if(FTPReply.isPositiveCompletion(reply)){ //connect an login successfully
    connected=true;    
   }
   else{
    Toast.makeText(context, "Can't connect to the server", Toast.LENGTH_SHORT).show();
   
   }
 
 
 

} catch (Exception e) {
   e.printStackTrace();
}

}


return connected;
    }
 
 
 
    public void showTransferUI(){    
   
    TransferFragment tf=TransferFragment.newInstance();
FragmentTransaction transact=getSupportFragmentManager().beginTransaction();
transact.replace(R.id.fragment_container, tf,"transferf");
transact.addToBackStack(null);
transact.commit();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
onResume();
//Toast.makeText(this, "count="+count, Toast.LENGTH_SHORT).show();

    }
 

 
    public void regControls(){
    //Toast.makeText(this, localpath, Toast.LENGTH_SHORT).show();
EditText txtlocal=(EditText) findViewById(R.id.txt_local);
if(txtlocal!=null){
txtlocal.addTextChangedListener(new TextLocalChangeListener());
txtlocal.setText(localpath);
//Toast.makeText(this, "NULL", Toast.LENGTH_SHORT).show();
}

EditText txtserver=(EditText) findViewById(R.id.txt_server);
if(txtserver!=null){
txtserver.addTextChangedListener(new TextServerChangeListener());
txtserver.setText(serverpath);

}
ListView localFileslist=(ListView) findViewById(R.id.localfiles_list);
if(localFileslist!=null){
localFileslist.setSelector(R.drawable.selection_style);
localFileslist.setOnItemClickListener(new ClickListener());
}

ListView serverFileslist=(ListView) findViewById(R.id.serverfiles_list);
if(serverFileslist!=null){
serverFileslist.setSelector(R.drawable.selection_style);
serverFileslist.setOnItemClickListener(new ServerClickListener());
}

}

   class TextLocalChangeListener 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_local);
localpath=et.getText().toString();    
localDirContents();
}

public void afterTextChanged(Editable ed){


}
}
 
   class ClickListener implements OnItemClickListener{
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      // selected item
     
           ViewGroup vg=(ViewGroup)view;
        TextView tv= (TextView)vg.findViewById(R.id.label);
        String selectedItem=tv.getText().toString();
           EditText et=(EditText) findViewById(R.id.txt_local);
           localpath=localpath+"/"+selectedItem;
           et.setText(localpath);          
         
      }
      public void onNothingSelected(AdapterView<?> parent){
     
      }
     
     
     }

 
    public void localDirContents(){
    ListView localfiles=(ListView) findViewById(R.id.localfiles_list);
    if(localpath!=null){
    try{
    File f=new File(localpath);
    if(f!=null){
    if(f.isDirectory()){
    String[] files=f.list();
    ArrayList<String> contents=toArrayList(files);
    if(contents.size()>0){
    ListAdapterModel lm=new ListAdapterModel(context,R.layout.listlayout,R.id.label,contents,localpath,null);
    localfiles.setAdapter(lm);
    }
    else
    {
    localpath=f.getParent();
    }
    }
    else{
    localpath=f.getParent();
    }
    }
    }catch(Exception e){}
    }    
 
   
    }
 
    public ArrayList<String> toArrayList(String[] contents){
    ArrayList<String> list=new ArrayList<String>();
    for(String f:contents){
    list.add(f);
    }
    return list;
    }
 
    class TextServerChangeListener 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_server);
    serverpath=et.getText().toString();
    BackTask bt=new BackTask();
    bt.execute(null,null,null);
    }
   
    public void afterTextChanged(Editable ed){
   
   
    }
    }
    class ServerClickListener implements OnItemClickListener{
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      // selected item
     
           ViewGroup vg=(ViewGroup)view;
        TextView tv= (TextView)vg.findViewById(R.id.label);
        String selectedItem=tv.getText().toString();
           EditText et=(EditText) findViewById(R.id.txt_server);
           serverpath=serverpath+"/"+selectedItem;
           et.setText(serverpath);          
         
      }
      public void onNothingSelected(AdapterView<?> parent){
     
      }
     
     
     }

        public void serverDirContents(){
        //get the files and directories from server
        if(!isServerFile(serverpath)){
        ftpobjects=filecontents(serverpath);
        if(ftpobjects.size()<=0){
        try {
        //record the working directory        
serverpath=client.printWorkingDirectory();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
        }
        }
        else{
        try {
        //record the working directory
serverpath=client.printWorkingDirectory();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
        }
       
     
        }
     
     
        public ArrayList<FTPFile> filecontents(String path){
        ArrayList<FTPFile> fileslist=new ArrayList<FTPFile>();
         
        try {
        //set the current working directory
client.changeWorkingDirectory(path);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
        try {
        //list directories
        FTPFile[] dirs=client.listDirectories();
    for(FTPFile d:dirs){
    fileslist.add(d);
    }
    //list files
    FTPFile[] files=client.listFiles();
    for(FTPFile f:files){
    fileslist.add(f);
   
    }

} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
        return fileslist;
       
        }
     

        public boolean isServerFile(String spath){
        boolean isFile=false;
        try {
FTPFile[] files = client.listFiles();
for(FTPFile f:files){
    if(f.getName().equals(getFileName(spath))){
    if(f.isFile()){ //the specified path is a file
    isFile=true;
    break;
         
    }
    }
   
    }
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
   
        return isFile;
        }
     
   public String getFileName(String path){
        String filename=path.substring(path.lastIndexOf("/")+1);
        return(filename);
        }
     
  //background process for retrieving file and directories from server
     
   private class BackTask extends AsyncTask<Void,Void,Void>{  
       
       
        protected void onPreExecute(){
        super.onPreExecute();
        //show process dialog
        pd = new ProgressDialog(context);
    pd.setTitle("Retrieving data");
    pd.setMessage("Please wait.");
    pd.setCancelable(true);
    pd.setIndeterminate(true);
    pd.show();
   
       
        }
        protected Void doInBackground(Void...params){    
        try{
        //retrieving files and directories from server
        serverDirContents();
        }catch(Exception e){
        pd.dismiss();   //close the dialog if error occurs
        }
    return null;
       
        }
       
       
       
        protected void onPostExecute(Void result){
        //close the progress dialog
        pd.dismiss();
        //show files and folders in the ListView
        ListView serverfiles=(ListView) findViewById(R.id.serverfiles_list);
        ArrayList<String> contents=getFTPNames(ftpobjects);
        if(contents.size()>0){
        ListAdapterModel lm=new ListAdapterModel(context,R.layout.listlayout,R.id.label,contents,"",ftpobjects);
    serverfiles.setAdapter(lm);
}
       
        }
       
        }
     
        //get files and directories from the server
     
        public ArrayList<String> getFTPNames(ArrayList<FTPFile> files){
        ArrayList<String> list=new ArrayList<String>();
        for(FTPFile f:files){
        list.add(f.getName());
        }
        return list;
        }
     
        public void uploadFile(View view){
        BackTaskUpload btupload=new BackTaskUpload();
        btupload.execute(null,null,null);
 
        }
     
      public void upload(){
      EditText txtLocalPath=(EditText)findViewById(R.id.txt_local);
      String filepath=txtLocalPath.getText().toString();
      File f=new File(filepath);
      String filename=f.getName();
      FileInputStream fis = null;
     
      if(f.isFile()){
      try {
fis = new FileInputStream(f);
client.storeFile(filename, fis);
fis.close();
     
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
fis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
      }
      }
    //background process for uploading file to server
 private class BackTaskUpload extends AsyncTask<Void,Void,Void>{  
       
       
        protected void onPreExecute(){
        super.onPreExecute();
        //show process dialog
        pd = new ProgressDialog(context);
    pd.setTitle("Uploading the file");
    pd.setMessage("Please wait.");
    pd.setCancelable(true);
    pd.setIndeterminate(true);
    pd.show();
   
       
        }
        protected Void doInBackground(Void...params){    
        try{
        //upload selected file to server
        upload();
        }catch(Exception e){
        pd.dismiss();   //close the dialog if error occurs
        }
    return null;
       
        }
       
       
       
        protected void onPostExecute(Void result){
        pd.dismiss();
}
       
        }
       
     
     
     
     
        public void downloadFile(View view){
        BackTaskDownload btdownload=new BackTaskDownload();
        btdownload.execute(null,null,null);
       
        }
        public void download(){
        FileOutputStream outfile=null;
        EditText txtServer=(EditText)findViewById(R.id.txt_server);
        String txtpath=txtServer.getText().toString();
        if(isServerFile(txtpath)){
            String filename=getFileName(txtpath);
           
            try {
    outfile=new FileOutputStream(localpath+"/"+filename);
    client.retrieveFile(filename, outfile);
   
   
    } catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }finally{
    try {
outfile.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
    }
            }
        }
   

        //background process for downloading file from server
        private class BackTaskDownload extends AsyncTask<Void,Void,Void>{  
       
       
        protected void onPreExecute(){
        super.onPreExecute();
        //show process dialog
        pd = new ProgressDialog(context);
    pd.setTitle("Downloading the file");
    pd.setMessage("Please wait.");
    pd.setCancelable(true);
    pd.setIndeterminate(true);
    pd.show();
   
       
        }
        protected Void doInBackground(Void...params){    
        try{
        //upload selected file to server
        download();
        }catch(Exception e){
        pd.dismiss();   //close the dialog if error occurs
        }
    return null;
       
        }
       
       
       
        protected void onPostExecute(Void result){
        pd.dismiss();
}
       
        }
     
     
        //save the last login info
     
        public void saveLogin(String domain,String username,String password){
       
        try {
        File file = new File(this.getFilesDir(), "ftplogin.info");
        FileWriter fw=new FileWriter(file);
        BufferedWriter bw=new BufferedWriter(fw);    
        bw.write(domain+"-"+username+"-"+password);
        bw.close();
       
        } catch (IOException e) {
        e.printStackTrace();
        }
       
        }
     
        //read the previous login info
     
        public String[] readLogin(){
    String[] info=null;
    try {
   
    File file = new File(this.getFilesDir(), "ftplogin.info");
    if(file.exists()){
    FileReader fr=new FileReader(file);
    BufferedReader br=new BufferedReader(fr);  
    info=br.readLine().split("-");
    br.close();
    }
   
    } catch (IOException e) {
    e.printStackTrace();
    }

    return info;
   }


 
}


When the uploader app firstly runs, the interface that the user sees is the log in form. So the sub-interface LoginFragment object is created and added to the container.

LoginFragment lf=new LoginFragment();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container,fl).commit();

The initLogin method will be called when the program starts to read the last log in information that contains domain name, username, and password to place on the log in form so that the user can connect to server without reentering the same log in information again (It saves your time).

The onResume method is overridden to register the controls or components on the TransferFragment sub-interface to text change events (EditText components) and Item click events (ListView components) by calling the regControls method.

The onDestroy method is also overridden to close the progress dialog (if it is still showing) and to disconnect from the remove server before the user leaves the app.

The connectToServer method is called when the Connect button on the log in form is pressed. This method subsequently calls the connect method to connect to the remove server. It returns true if the app successfully connects to the remote server. Otherwise it returns false. The connect method of the FTPClient class is used to connect to the remove server. You need to supply to this method the ftp domain name (e.g. ftp.worldbestlearningcenter.com). To log in to the remote server, you will use the login method. This method takes username and password. You must provide the correct username, and password to login in to the server. Generally, you will use the passive mode data connection to transfer files between the client and the server. To set the passive mode for the data connection, you will use the enterLocalPassiveMode method method.

To test whether you successfully connect to the server, you will use the isPositiveCompletion method of the FTPReply class. This method takes the reply code to be its argument. You can get the reply code by using the getReplyCode method of the FTPClient class.

If the connection is successful,  the connectToServer method subsequently calls the showTranserUI method to display the file transfer sub-interface. In the showTranserUI method, the TransferFragment object is created and added to the container. It replaces the first sub-interface (LoginFragment).

The local text box (txt_local) displays the selected local file or directory. It also allows you to enter or edit the file or directory path. Every time the text of the txt_local changes, the contents of the local list also changes by calling the localDirContents method. This text box registers with the text change listener event when the regControls method is called. The ChangeListener extends the TextWatcher interface to handle the text change event of the text box.

When the user selects a file or directory name from the the local list, the file or directory name will be appended to the txt_local text box (making change to the text box) so that the contents of the local list are updated accordingly. The ListView local list (localfiles_list) is registered with the item click event by using the setOnItemClickListener method. This method takes an object of the ClickListener class that implements the OnItemClickListener inerface.

The text box and list for the server side (txt_server and serverfiles_list) work the same as the text box and list of the local side. However, since retrieving the files and directories from the server can take long time, the serverDirContents that is called every time the txt_server changes will be called from the AsyncTask class. This will place the files and directories retrieving task in the background process so that the user interface is not locked or is still responsive when the process goes on. To more explanation about using the AsyncTask to do background task, you will visit the KeywordDensityCheckter app. The ProgressDialog component is used at the beginning of the file and directories retrieving process to inform the user that the process is going on and need to wait.




Each list (either local side or server side) has the same selection style. The layout file that applies the selection style to the list is stored in the drawable directory of the uploader project and written as shown below.

selection_style.xml file

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
 
    <item>
        <shape>
            <gradient
                android:startColor="#dfdfdf"
                android:endColor="#dfdfdf"            
                android:angle="180" />
        </shape>      
    </item>
</selector>

The uploadFile method is invoked when the user presses the upload icon to upload the selected file to the remote server. The FTPClient has a method called storeFile that can be used to upload the file to the server. This method takes two arguments. One argument is the filename to be stored on the server. The second argument is the InputStream object that contains the data of the local file. The uploading file process can take long time so it is also placed as a background process by using the AsyncTask class.

Uploader for Android-upload file to server

The downloadFile method works similar to the uploadFile method except that it transfers the file from server to the local or client side. You will use the retrieveFile method of the FTPClient class to download the file from the server. This method takes two arguments. One is the filename from the server and another one is the OutputStream object to receive the data of the file and write it to the local storage.


Uploader for Android-download file from server


Before running the uploader for Android app, you need to make change to the AndroidManifest.xml file to allow the app to use the device storage and internet. This is the content of the AndroidManifest.xm file.

AndroidManifest.xm file

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

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 

    <application
        android:allowBackup="true"
        android:icon="@drawable/and_ftp"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
     
        <activity
            android:name="com.example.andftp.MainActivity"
            android:label="@string/app_name"
            android:configChanges="orientation"
             >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>


Another file that you need to make change is strings.xml file. It contains global string variables used in the app. Its content is shown below.

strings.xml file

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

    <string name="app_name">AndFTP</string>
    <string name="action_settings">Settings</string>
    <string name="hello_world">Hello world!</string>
    <string name="txt_username">Enter username</string>
    <string name="txt_password">Enter password</string>
    <string name="bt_connect">Connect</string>
    <string name="bt_upload">Upload</string>
    <string name="bt_download">Download</string>
    <string name="txt_local">Enter local path</string>
    <string name="txt_server">Enter server path</string>
    <string name="txt_ftpserver">Enter ftp server host</string>
</resources>


Now you are ready to run the uploader for Android app. If you have any questions, please leave them at the comments part below.

Download apk file of the Uploader for Android app


compass app flashLight app

Monday, October 7, 2013

Keyword Density Checker

There are many keyword density checkers on the web. The novice webmasters always use the keyword density checker to get information about their web page content. A good keyword checker helps the webmaster to have pages that conform to SEO (Search Engine Optimization) guidelines so that their web pages will get high ranks from the search engines. Many SEO experts consider the optimum keyword density to be 1 to 3 percent. The percent that is greater than this will be considered as spam and be penalized by the search engines.

In this post, you will learn to create a simple app to be used to check keywords density of a web page from Android devices. I called this project KeywordDensityChecker. You need to create a project in Eclipse and name it KeywordDensityChecker. The KeywordDensityChecker reads content of web page and analyze it to produce a keyord density table that shows keywords, frequencies, and percentages. The frequency of each keyword represents the number of the same keywords found in the web page. Its percentage is calculated by multiplying 100 to the divisional result of the frequency and the total keywords analyzed.
For the user interface of this simple keyword density checker, we need one EditText, one Button, and one ListView. The EditText allows the user to enter a web page address. The Button will be pushed to read the content of the web page and shows the analyzed result on the ListView. These views or components are defined in the activity_main.xml file that is the resource of the Main_Actity class.

activity_main.xm 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"
    tools:context=".MainActivity"
    android:orientation="vertical"

     >

  <EditText
        android:id="@+id/txt_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"
        android:hint="Enter web page adress"
        />
     
<Button
          android:id="@+id/bt_extract"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:text="Check"
          android:onClick="check"
          />

 
        <ListView
            android:id="@+id/density_list"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="5dp"
            android:paddingTop="5dp"
     
            />    

</LinearLayout>


The ListView displays keywords, frequencies, and percentages. Thus, the ListView needs to be customized to show these three items. The layout file (listlayout.xml) of the ListView will have three TextView components defined. One TextView displays the keyword; another one displays the frequency; and the last one displays the percentage.

keyword density checker



The content of the listlayout.xml file is shown below.

listlayout.xml file

<?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="wrap_content"
    android:orientation="horizontal"
    android:padding="5dip" >

<TextView
    android:id="@+id/keyword"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#0000ff"
        android:textStyle="bold"
       
         >
</TextView>

<TextView
        android:id="@+id/count"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#0000ff"
 
        android:textStyle="bold" >
</TextView>

<TextView
    android:id="@+id/percent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#0000ff"
   
        android:textStyle="bold" >
</TextView>
</LinearLayout>


Now we take a look at the MainActivty.java file that contains the MainActivity class. This will be the start point of our KeywordDensityChecker app. In this class, the check method is defined. The check method will be called when the Check button is pushed. In this method, we reference to te EditText address and the density_list views. The Checker object is created. All methods are used to read content of the web page, analyze, and prepare the necessary data (keywords, frequencies, and percentages) to construct the keyword density table are defined in the Checker class.

MainActivity.java file

package com.example.keyworddensitychecker;

import android.os.AsyncTask;
import android.os.Bundle;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;


public class MainActivity extends Activity {

private Context context;
private ListView list;
private Checker checker;
ProgressDialog pd;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context=this;      
    }
 
    @Override
    protected void onDestroy() {
    if (pd!=null) {
pd.dismiss();

}
    super.onDestroy();
    }

    @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 void check(View view){
    EditText txtaddress=(EditText)findViewById(R.id.txt_input);
    String address=txtaddress.getText().toString();
    list=(ListView)findViewById(R.id.density_list);
        checker=new Checker();
    if(address.length()>0){
    BackTask bt=new BackTask();
    bt.execute(address,null,null);
    }
    else
    Toast.makeText(context, "Please input web page address", Toast.LENGTH_SHORT).show();
    }
 
    private class BackTask extends AsyncTask<String,Void,Void>{  
   
   
    protected void onPreExecute(){
    super.onPreExecute();
    //show process dialog
    pd = new ProgressDialog(context);
pd.setTitle("Checking...");
pd.setMessage("Please wait.");
pd.setCancelable(true);
pd.setIndeterminate(true);
pd.show();
   
   
    }
    protected Void doInBackground(String...params){    
    try{
    //start analyzing the web page
    checker.processChecking(params[0]);
    }catch(Exception e){
    pd.dismiss();   //close the dialog if error occurs
    }
return null;
   
    }
   
   
   
    protected void onPostExecute(Void result){
    //close the progress dialog
    pd.dismiss();
    //create ListAdapterModel object for the ListView so that
    //the keyword density table can be displayed
    ListAdapterModel lam=new ListAdapterModel(context,R.layout.listlayout,checker.getWords(),checker.getCounts(),checker.getPercents());
    list.setAdapter(lam);
    }
   
    }



 
}


Reading content of the web page, analyzing it, and displaying the result can take long time. So it is a good idea to place these processed in background thread. In the previous post (Web Downloader), you learn to use the IntentService class to put the web downloading process in background thread so that it runs without locking the user interface. In this KeywordDensityCheck, you learn another tool to do the background process. This tool is called AsyncTask. The AsyncTask has three important methods--onPreExecute, doInBackground, and onPostExecute. Int the onPreExecute mehtod, you can write code to do something before the background process starts. In this app, the ProgressDialog is displayed to tell the user to wait.

Progress Dialog to wait while analyzing the web page


In the doInBackground method, you will place code that represents the background process. In this case, the proessChecking method of the Checker class is called to read the content of the web page, analyze it, and prepare the necessary data to construct the keyword density table. After the background process completes you can write code to do something in the onPostExecute method. In our KeywordDensityChecker app, the task to do after the background process completes is closing the progress dialog and constructing the ListAdapterModel object for the ListView component so that the keyword density table can be shown to the user.
The ListView component needs the ArrayAdapter object be its data source. In default, the ListView can display only a single TextView component. In this app, the ListView needs three TextView components. The first step to customize the ListView is to define the three TextView components in the layout file as i mentioned above. The additional step to customize the ArrayAdapter to enable the ListView to show the keyword density table that consists of keywords, frequencies, and percentages. Generally, to customize the ArrayAdapter class, you need to extend it. The ListAdapterModel extends the ArrayAdapter class. The important part of the ArrayAdapter class that must be overridden is the getView method. In this method, code is written to allow the ListView to show the keyword on the keyword TextView, frequency on the count TextView, and the percentage on the percent Textview. Below is the content of the ListAdapterModel class.

ListAdapterModel.java file

package com.example.keyworddensitychecker;

import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class ListAdapterModel extends ArrayAdapter<String>{
int groupid;
ArrayList<String> words;
Context context;
ArrayList<String> counts;
ArrayList<String> percents;

public ListAdapterModel(Context context, int vg,ArrayList<String> words,ArrayList<String> counts,ArrayList<String> percents){
super(context,vg, words);
this.context=context;
groupid=vg;
this.words=words;
this.counts=counts;
this.percents=percents;
}
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);
        TextView textKeyword = (TextView) itemView.findViewById(R.id.keyword);
        TextView textCount= (TextView) itemView.findViewById(R.id.count);
        TextView textPercent= (TextView) itemView.findViewById(R.id.percent);
        textKeyword.setText(words.get(position));
        textCount.setText(counts.get(position));
        textPercent.setText(percents.get(position));
        return itemView;
     
}

}


The last code fragment that defines the backgroud process is in the Checker class. In this class, there are four methods. The first method ,readWords will be called to read the content of the web page, split the content in to words. Each word is separated by a single space or many spaces. The split method is used to split the content in to words. A word might contain symbols. The regular expression is used to remove all symbols from the words. The Pattern class is used to define string pattern ("\\W+") to match all symbols (except underscore) and the replaceAll method of the Matcher is called to remove all the symbols from the words. To learn more about regular expression in Java, read this page. The words are filtered again to ignore the words that are not keywords. The filtering task is performed by invoking the addToList method. The addToList method adds every keyword to the LinkedList, keywordsList. The countWords method is invoked to count the frequency of each keyword. This method also adds the every keyword and its frequency to the HashMap, denMap. The prepareTable method prepares data necessary to construct the keyword density table. The keywords will be stored in the ArrayList, keywords; the frequencies are stored in the ArrayList, counts; and the percentages are stored int he ArrayList, percents. The processChecking method wraps the readWords, countWords, and prepareTable methods so that they can be called at once. The getWords, getCounts, and getPercents methods are called from the MainActivity class to return the list of keywords, the list of frequencies, and the list of percents to be used in the ListAdapterModel class. The content of the Checker class is shown below.

Checker.java file

package com.example.keyworddensitychecker;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


class Checker{
private String address;
private TreeMap<String, Integer> denMap;
private LinkedList<String> keywordsList;
private ArrayList<String> keywords;
private ArrayList<String> counts;
private ArrayList<String> percents;

Checker(){
denMap=new TreeMap<String,Integer>();
keywordsList=new LinkedList<String>();
keywords=new ArrayList<String>();
counts=new ArrayList<String>();
percents=new ArrayList<String>();
}

public void readKeywords(){
Pattern pattern=Pattern.compile("\\W+");
try {
//read the web page content
URL url=new URL(address);
HttpURLConnection con=(HttpURLConnection)url.openConnection();
InputStream is=con.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
String strLine;
while((strLine=br.readLine())!=null){
String[] words=strLine.split("[ ]+"); //split content in to words
for(String word:words){
Matcher mat=pattern.matcher(word); //remove all symbols except underscore
word=mat.replaceAll("");
addToList(word.toLowerCase());
}
}

br.close();
} catch (Exception ex) {
// TODO Auto-generated catch block
ex.printStackTrace();
}
}

public void addToList(String word){

//filter non-keywords
//there are more non-keywords that are not filtered in this simple program
//you can add those non-keywords in the nonKeyword array below.
boolean isNonKey=false;
String[] nonKeywords={"a","an","after","and","are","as","at","above","before"
,"below","beyond","br","div","for","in","is","li","of","on","p","the"
,"span","that","this","those","tr","td","to","ul","under","when","where","you"};
for(String nonKey:nonKeywords){
if(nonKey.equals(word)){
isNonKey=true;
break;
}
}
//add only the keyword to the list
if(!isNonKey)
keywordsList.add(word);
}


public void countKeywords(){
int count=1;
String word="";
for(int i=0;i<keywordsList.size();i++){
word=keywordsList.get(i);
for(int j=i+1;j<keywordsList.size();j++){
if(word.equals(keywordsList.get(j))){
count++;
}
}

addToMap(word,count);
count=1;
}

}

public void addToMap(String word, int count){
//place keyword and its frequency in TreeMap
if(!denMap.containsKey(word) && word.length()>=1){
denMap.put(word, count);
}

}


public void prepareTable(){

Set<String> keys=denMap.keySet();
int numWord=keys.size();
Iterator<String> iterator=keys.iterator();
while(iterator.hasNext()){
String word=iterator.next();
int count=denMap.get(word);
int p=100*count/numWord;
if(p>0){
keywords.add(word);
counts.add(String.valueOf(count));
percents.add(p+"%");

}
}



}

public void processChecking(String address){

this.address=address;
readKeywords();
countKeywords();
prepareTable();
}

public ArrayList<String> getWords(){
return keywords;
}
public ArrayList<String> getCounts(){
return counts;
}
public ArrayList<String> getPercents(){
return percents;
}


}


Before starting to run the KeywordDensityChecker app, you need to allow Android to use the internet by placing the below code to the AndroidManifest.xml file.

<uses-permission android:name="android.permission.INTERNET"/>

Download the apk file of the KeywordDensityChecker app

Wednesday, October 2, 2013

Currency Converter

In this post, you learn to create a currency converter app. This currency converter can be used to convert twelve currency types. The currency types that the converter supports are shown below.

-AUD (Australian Dollar)
-CAD (Canadian Dollar)
-CHF (Swiss Franc)
-EUR (Euro)
-GBP (British Pound)
-JPY (Japnese Yen)
-NZD (New Zealand Dollar)
-KHR (Khmer Riel)
-USD (American Dollar)
-CNY (Chinese Yuan Renminbi)
-THB (Thai Baht)
-INR (Indian Rupee)

To follow this tutorial, you will need to create a new Android project in Eclipse. The project will be named CurrencyConverter.

In the CurrencyConverter app, the user is allowed to select a pair of currency for the conversion. One is the currency that will be converted from and another is the currency that will be converted to. We also allow the user to input the exchanged rate and the amount to be converted. One button is provided to show the table of conversion. The table displays the currency types and amounts. The currency types that are not the target of the conversion, their amounts are calculated based on their default average exchange rates. For target currency type, its amount is calculated based on the exchange rate provided by the user. The target currency is highlighted.

currency converter main interface


In this CurrencyConverter app, there are two activities. On activity is the main activity that starts when the app initially runs. The components or view that are used to construct the user interface of the main activity are two TextViews, two Spinner, two EditTexts, and one Button. The two TextViews to display the labels of the Spinners. The two Spinners allow the user to select a currency type to convert from and another currency type to converted to. The spinner_style.xml file defines the item style of the Spinners. One EditText is for the user to input the exchange rate and another one allows the user to input the amount for converting. The Show button will be pushed to display the conversion table. This button has background style that defined by the bt_style.xml file. The activity_main.xml file that is the resource of main activity is written as shown below.

activity_main.xml
nightmode  app Digital clock tdve wallpaper app

<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"
    android:background="#ff8899"
    tools:context=".MainActivity" >
<LinearLayout
   android:layout_marginTop="10dp"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
    <TextView
        android:id="@+id/from_currency"
        android:layout_width="150dp"
    android:layout_height="wrap_content"
    android:textColor="#ffffff"
    android:textSize="18sp"
        android:text="@string/from_currency" />
    <Spinner
android:id="@+id/fromcurrency_spin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

/>
</LinearLayout>
<LinearLayout
   android:layout_marginTop="10dp"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"      
        android:orientation="horizontal" >
<TextView
        android:id="@+id/to_currency"
        android:layout_width="150dp"
    android:layout_height="wrap_content"
    android:textColor="#ffffff"
    android:textSize="18sp"
        android:text="@string/to_currency" />
    <Spinner
android:id="@+id/tocurrency_spin"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

/>
</LinearLayout>

<EditText
        android:id="@+id/txt_rate"
        android:layout_width="match_parent"
    android:layout_height="wrap_content"
        android:hint="@string/txt_rate"
        android:inputType="numberDecimal" />

<EditText
        android:id="@+id/txt_fromamount"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/txt_fromamount"      
        android:inputType="numberDecimal" />

<Button
   android:id="@+id/bt_show"
   android:background="@drawable/bt_style"
   android:text="@string/bt_label"
   android:layout_width="match_parent"
   android:layout_height="40dp"
   android:gravity="center"
   android:onClick="showResult"
   />


</LinearLayout>


spinner_style.xml

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

<CheckedTextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    style="?android:attr/spinnerDropDownItemStyle"
    android:singleLine="true"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:ellipsize="marquee"
 
     />

bt_style.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" >
        <shape>
            <solid
                android:color="#ff00ff" />
            <stroke
                android:width="1dp"
                android:color="#171717" />
            <corners
                android:radius="3dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>
    <item>
        <shape>
            <gradient
                android:startColor="#ffffff"
                android:endColor="#992211"
                android:angle="270" />
            <stroke
                android:width="1dp"
                android:color="#171717" />
            <corners
                android:radius="4dp" />
            <padding
                android:left="10dp"
                android:top="10dp"
                android:right="10dp"
                android:bottom="10dp" />
        </shape>
    </item>
</selector>

The main activity is defined by the MainActivity class. Besides loading the components from the activity_main.xml file, the MainActivity has code to set up the item data of the Spinners. This task is done by calling the setupSpinnerData method. To capture the From currency and To currency, the two Spinners has to register to the items selected event. When the user selected a currency from the first Spinner, the selected currency is stored in the fromCurrency variable. Similarly, the selected currency from the second Spinner is stored in the toCurrency variable.

The showResult result method is invoked when the user pushes the Show button. Before showing the conversion table, the app checks to make sure that the exchange rate text box and the amount text are not blank. The alert dialog box is displayed when he/she tries to click the Show button with the blank exchange rate or amount. The intent object is created to put some data such as From currency, To currency, exchange rate, and amount. The intent object that contains the data is sent to the second activity after the startActivity method is invoked. Below is the content of the MainActivity.java file that has the MainActivity class.

MainActivity.java

package com.example.currencyconverter;

import android.os.Bundle;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends Activity {

   private String fromCurrency;
   private String toCurrency;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }


    @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;
    }
 
 
 
    protected void onResume(){
    super.onResume();
    setUpSpinnerData();
       
    }
    //This method will be invoked to setup data of the spinner views
    //to show lists of currency types for selection
    public void setUpSpinnerData(){
    Spinner spFrom=(Spinner)findViewById(R.id.fromcurrency_spin);
    Spinner spTo=(Spinner)findViewById(R.id.tocurrency_spin);
    String[] currencyList={"AUD","CAD","CHF","EUR","GBP","JPY","NZD","KHR","USD","CNY","THB","INR"};
    ArrayAdapter<String> afrom=new ArrayAdapter<String>(this,R.layout.spinner_style,currencyList);
    spFrom.setAdapter(afrom);
    spFrom.setOnItemSelectedListener(new ItemSelectedFrom());
    ArrayAdapter<String> ato=new ArrayAdapter<String>(this,R.layout.spinner_style,currencyList);
    spTo.setAdapter(ato);
    spTo.setOnItemSelectedListener(new ItemSelectedTo());
   
    }
 
    private class ItemSelectedFrom implements OnItemSelectedListener{
    public void onNothingSelected(AdapterView<?> av){
   
    }
    public void onItemSelected(AdapterView<?> av, View view, int position, long id){
    TextView sel=(TextView)view;
    String from=sel.getText().toString();
    fromCurrency=from; //capture the currency of the From side
        EditText txtfrom=(EditText)findViewById(R.id.txt_fromamount);
    txtfrom.setHint("Enter "+fromCurrency+" amount");
   
    }
    }
 
    private class ItemSelectedTo implements OnItemSelectedListener{
    public void onNothingSelected(AdapterView<?> av){
   
    }
    public void onItemSelected(AdapterView<?> av, View view, int position, long id){
    TextView sel=(TextView)view;
    String to=sel.getText().toString();
    toCurrency=to; //capture the currency of the To side
       
   
    }
    }
 

    public void showResult(View view){
    EditText txtRate=(EditText)findViewById(R.id.txt_rate);
    EditText txtAmount=(EditText)findViewById(R.id.txt_fromamount);
    if(txtRate.getText().toString().length()<=0 || txtAmount.getText().toString().length()<=0){
    AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Please input value in text box.");
            builder.setCancelable(true);
            builder.setPositiveButton("OK", new OnClickListener(){
            public void onClick(DialogInterface di,int which){
            di.cancel();
            }
            });
            AlertDialog dialog = builder.create();      
            dialog.show();

    }
    else{
    //create intent, place data in it and start the ConversionTable activity
    Intent intent=new Intent(this, ConversionTable.class);
    intent.putExtra("fromCurrency", fromCurrency);
    intent.putExtra("toCurrency", toCurrency);
    intent.putExtra("Rate", Double.valueOf(txtRate.getText().toString()));
    intent.putExtra("fromAmount", Double.valueOf(txtAmount.getText().toString()));
    startActivity(intent);
    }
    }
 
}


Another activity is called ConversionTable. It shows when the user pushes the Show button. This activity displays the conversion table as i mentioned above. This activity has a resource xml file called activity_conversion_tabl.xml. This file defines one ListView component to show the conversion table.

activity_conversion_tabl.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android: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=".ConversionTable" >

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

</LinearLayout>


When constructing the conversion table, you will need the icon images of the countries that their currency types are used in the CurrencyConverter app. You can download the icon images from here.
In default, ListView in Android displays on text. However, this CurrencyConverter app, the conversion table has to display icon images, currency types, and amount text. We have to customize the ListView to do this task. To customize the ListView, you start from defining the components for its item. The ListView has its own layout file called listlayout.xml that defines the layout of its item or row. The content of the listlayout.xml file is show below.

listlayout.xml

<?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:background="#000000"
    android:padding="5dip" >

<ImageView
    android:id="@+id/icon"
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:padding="5sp"
    android:contentDescription="countryicon"
   />

 <TextView
    android:id="@+id/currency"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#ffffff"
        android:textStyle="bold" >
</TextView>
<TextView
    android:id="@+id/label_amount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#ffffff"
        android:textStyle="bold" >
</TextView>
</LinearLayout>

In this listlayout file, we define three components-- one ImageView, and two TextViews. The ImageView will display the icon image. One TextView displays the currency type, and another TextView displays the amount.

Next, you need to extends the ArrayAdapter class that acts as the data source of the ListView. This class is called ListAdapterModel. Its getView method is overridden to allow the ListView to display icon image, currency type, and amount.

ListAdapterModel.java

package com.example.currencyconverter;
import java.text.DecimalFormat;
import android.content.Context;
import android.graphics.Color;
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;
String[] clist;
Context context;
String to;
Double[] alist;

public ListAdapterModel(Context context, int vg,String[] clist, Double[] alist, String to){
super(context,vg, clist);
this.context=context;
groupid=vg;
this.clist=clist;
this.to=to;
this.alist=alist;
}
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.setImageDrawable(context.getResources().getDrawable(getImage(position)));
        TextView textCurrency = (TextView) itemView.findViewById(R.id.currency);
        TextView textAmount= (TextView) itemView.findViewById(R.id.label_amount);
        textCurrency.setText(clist[position]);
        DecimalFormat df=new DecimalFormat("#,###.0000");
        String value=df.format(Double.valueOf(alist[position]));
        textAmount.setText(value);
        if(clist[position].equals(to)) //highlight the target conversion row
        itemView.setBackgroundColor(Color.MAGENTA);
        return itemView;
     
}

public Integer getImage(int pos){
Integer[] imageIds=new Integer[12];
imageIds[0]=R.drawable.aud;
imageIds[1]=R.drawable.cad;
imageIds[2]=R.drawable.chf;
imageIds[3]=R.drawable.eur;
imageIds[4]=R.drawable.gbp;
imageIds[5]=R.drawable.jpy;
imageIds[6]=R.drawable.nzd;
imageIds[7]=R.drawable.khr;
imageIds[8]=R.drawable.usd;
imageIds[9]=R.drawable.cny;
imageIds[10]=R.drawable.thb;
imageIds[11]=R.drawable.inr;

return(imageIds[pos]);

}

}


Inside the ConversionTable class, Java code is written to construct the conversion table. The constructTable method is called to prepare the data for the ListView and display the currency conversion table. The data to be defined here are the array of amounts (amounts) and the currency list (clist). Each amount that is the item of the amounts array is calculated based on this formula:

amounts[i]=rate*amount;

The default average exchange rates are used for the currency types that are not the target. For the target conversion currency, the rate input by the user will be used. The clist array contains all currency types that are supported by the CurrencyConverter app.

The amounts array is used by the ListAdapterModel class to display the amounts and the clist array contains the currency types to show with the amount on the conversion table. The icon image list is not defined here. It is in the ListAdapterModel class.

currency converter conversion table sub interface


The AndroidManifest.xml file is modified to allow the second activity to be the child of the main activity. When you are on the second activity and press the back button, you will come back to the main activity. The content of the AndroidManifest.xml file is shown below.

AndroidManifest.xml

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

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/currencyconverter"
        android:label="@string/app_name"
     
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.currencyconverter.MainActivity"
            android:label="@string/app_name"
            android:configChanges="orientation"
             >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.example.currencyconverter.ConversionTable"
            android:label="@string/title_activity_conversion_table"
             android:configChanges="orientation"
            android:parentActivityName="com.example.currencyconverter.MainActivity"          
             >
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.example.currencyconverter.MainActivity" />
         
            >
        </activity>
     
    </application>

</manifest>


Now you are ready to run the program. If you have any questions, please write them at the comment section. I will reply as soon as possible. Thank you for reading this post.

Merge or Combine PDF, Txt, Images