Pages

Showing posts with label android app. Show all posts
Showing posts with label android app. Show all posts

Tuesday, October 15, 2013

File Locker for Android

In this tutorial, you will learn to create a File Locker app for Android devices. The File Locker app displays a list of files that the user can select to lock or unlock. The directories also list so that the user is able to navigate from one directory to another. To lock or unlock a file, the user has to select the file and enter the password in to the password text box. Then the user will push the Lock button to lock the file or push the Unlock button to unlock the file.




When the file is locked, the locked icon will display next to the file (on the left) when its parent directory is refreshed. The file that is already locked is not allowed to lock again unless it is unlocked. To unlock the file the user must provide the correct password. It is the password that he/she uses to lock the file.

The user can choose to lock any file. The locked file will be not understandable since its content is encrypted. The encryption and decryption processes are performed on the file.The performance (speed of the processes) of the app still be maintained although the file is very large (e.g audio, video, and image files) . I will talk about this technique later in this post.

Now open your Eclipse and create a new project. The project name will be FileLocker. On the main interface, an EditText component is needed to allow the user to input the password. We need two Buttons. One is for locking the file and another one is for unlocking the file. We also need a ListView to display the list of files and directories. These components are defined in the activity_main.xml file that is the resource file of the MainActivity class. Here is its content.

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"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:background="@drawable/back_style"
     >

  <EditText
        android:id="@+id/txt_input"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="textPassword"    
        android:hint="@string/txt_hint"
        />
        <LinearLayout
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:orientation="horizontal"
             >
<Button
          android:id="@+id/bt_lock"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/label_lock"
          android:onClick="lockFile"
          />
  <Button
          android:id="@+id/bt_unlock"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/label_unlock"
          android:onClick="unlockFile"
          />
  </LinearLayout>
   <TextView
    android:id="@+id/txt_view"
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
      />
        <ListView
            android:id="@+id/files_list"
            android:layout_width="fill_parent"
            android:layout_height="300dp"
            android:paddingBottom="5dp"
            android:paddingTop="5dp"
     
            />    

</LinearLayout>


The file that applies background style to the interface is called back_style.xml. It is saved in the drawable directory of the project. In this directory, you also need some icon images that are used in the File Locker app. You can download the content of the drawable directory from here.

back_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 string values that are used in the activity_main.xml file are defined in the strings.xml file. This is the content of the strings.xml file.

strings.xml file

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

    <string name="app_name">FileLocker</string>
    <string name="action_settings">Settings</string>
   <string name="label_lock">Lock</string>
   <string name="txt_hint">Enter password</string>
<string name="label_unlock">Unlock</string>
<string name="icon_image">Icon</string>

</resources>



The ListView has its own layout file. This file defines the template for an item of the list. An item of the list contains two parts. One part is the icon. It can be file icon, directory icon, or locked icon. Another part is the file or directory name. This layout file is called listlayout.xml file stored in the layout directory.

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

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


In default the ListView component displays only text. To enable the ListView to display both images and text, you need to customize it. The ListView customization starts from defining its layout file to include two components--image and text as you see in the listlayout.xml file shown above. Another step is to extend the ArrayAdapter class so that image and text can be placed on the components. The ListAdapterModel is created to accomplish this task. The ListAdapterModel object will contain data required to display on the list.

ListAdapterModel.java file

package com.example.filelocker;

import java.io.File;

import android.content.Context;
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[] names;
Context context;
String path;
public ListAdapterModel(Context context, int vg, int id, String[] names, String parentPath){
super(context,vg, id, names);
this.context=context;
groupid=vg;
this.names=names;
this.path=parentPath;
}
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=names[position];
        textView.setText(item);
        File lockedfile=new File(context.getFilesDir(),item);
        if(lockedfile.exists()){
        //set the locked icon to the file that was already locked.
        imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.locked_icon));
        }
        else{//set the directory and file icon to the unlocked file
        File f=new File(path+"/"+item);
        if(f.isDirectory())
        imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.diricon));
        else
        imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.fileicon));
        }
        return itemView;
}

}


To help us in locking and unlocking file processes, we will need a Locker class. The Locker.java file defines a class called Locker. This class contains methods that will be used in locking and unlocking file process.

Locker.java file

package com.example.filelocker;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import android.content.Context;
import android.webkit.MimeTypeMap;

class Locker{
String path;
String pwd;
Context context;
final String separator="--*****--";

Locker(Context context,String path,String pwd){
this.path=path;
this.pwd=pwd;
this.context =context;
}


public boolean isTextFile(String file){

boolean isText=false;
String extension = MimeTypeMap.getFileExtensionFromUrl(file);
   String mimeType=MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
   if(mimeType.startsWith("text/"))
    isText=true;
        return isText;
}

public void lock(){
boolean isHead=true;
boolean isBody=false;
int blockSize=0;

try{
File f=new File(path);
//get previous pwd
if(f.exists()){
byte[] ppwd=getPwd();
if(ppwd!=null){
MessageAlert.showAlert("Alreadly locked",context);
return;
}
FileInputStream fis=new FileInputStream(f);
File tempfile=new File(context.getFilesDir(),"temp.temp");
FileOutputStream fos=new FileOutputStream(tempfile);
FileChannel fc=fis.getChannel();
int pwdInt=bytearrayToInt(pwd.getBytes());
int nRead;
boolean isText=isTextFile(path);
if(isText){ //encrypting two parts of the text file
blockSize=(int)f.length()/4; //25 percent of the file content
ByteBuffer bb=ByteBuffer.allocate(blockSize);

while ( (nRead=fc.read( bb )) != -1 )
{
bb.position(0);
bb.limit(nRead);

//encrypt the head section of the file
if(isHead){
while ( bb.hasRemaining())
fos.write(bb.get()+pwdInt);
isHead=false;
isBody=true;
}
else if(isBody){
//do not decrypt the body section of the file
fos.write(bb.array());
isBody=false;
}
else{//encrypt the footer section of the file
while ( bb.hasRemaining())
fos.write(bb.get()+pwdInt);
}

bb.clear();

}
}

else{
blockSize=1024; //encrypt the first 1kb of the file for non-text file
ByteBuffer bb=ByteBuffer.allocate(blockSize);

while ( (nRead=fc.read( bb )) != -1 )
{
bb.position(0);
bb.limit(nRead);
//encrypt only the head section of the file
if(isHead){
while ( bb.hasRemaining())
fos.write(bb.get()+pwdInt);
isHead=false;

}
else{
fos.write(bb.array());
}
bb.clear();

}
}

fis.close();
fos.flush();
fos.close();
//replacing the file content
f.delete();
File lockedFile=new File(path);
copyFile(tempfile,lockedFile);
//delete the temp file
tempfile.delete();
//save the password
saveInfo(pwd,blockSize);
//make the file read only
lockedFile.setReadOnly();

}

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


public void unlock(){
boolean isHead=true;
boolean isBody=false;
int pwdread=bytearrayToInt(getPwd());
int pwdbyte=bytearrayToInt(pwd.getBytes());
if(pwdbyte==pwdread){

try{
File f=new File(path);
if(f.exists()){

FileInputStream fis=new FileInputStream(f);
File tempfile=new File(context.getFilesDir(),"temp.temp");
FileOutputStream fos=new FileOutputStream(tempfile);
FileChannel fc=fis.getChannel();
int pwdInt=bytearrayToInt(pwd.getBytes());
int blockSize=getBlockSize();
ByteBuffer bb=ByteBuffer.allocate(blockSize);
int nRead;
boolean isText=isTextFile(path);
if(isText){ //decoding two parts of the text file
while ( (nRead=fc.read( bb )) != -1 )
{
bb.position(0);
bb.limit(nRead);

//decrypt the head section of the file
if(isHead){
while ( bb.hasRemaining())
fos.write(bb.get()-pwdInt);
isHead=false;
isBody=true;
}
else if(isBody){
//do not decrypt the body section of the file
fos.write(bb.array());
isBody=false;
}
else{//decrypt the footer section of the file
while ( bb.hasRemaining())
fos.write(bb.get()-pwdInt);
}

bb.clear();

}
}

else{

while ( (nRead=fc.read( bb )) != -1 )
{
bb.position(0);
bb.limit(nRead);
//encrypting only the head section of the file
if(isHead){
while ( bb.hasRemaining())
fos.write(bb.get()-pwdInt);
isHead=false;

}
else{
fos.write(bb.array());
}
bb.clear();

}
}

fis.close();
fos.flush();
fos.close();
//Replacing the file content
f.delete();
File unlockedFile=new File(path);
unlockedFile.setWritable(true);
copyFile(tempfile,unlockedFile);
//delete the temp file
tempfile.delete();
File filepwd=new File(context.getFilesDir(),getName(path));
//delete the password
filepwd.delete();



}

}catch(IOException e){e.printStackTrace();}
}
else{
MessageAlert.showAlert("Invalid password or file is not locked.",context);
}
}

private void copyFile(File src, File dst) throws IOException
{
FileInputStream fis=new FileInputStream(src);
FileOutputStream fos=new FileOutputStream(dst);
    FileChannel inChannel =fis.getChannel();
    FileChannel outChannel = fos.getChannel();
    try
    {
        inChannel.transferTo(0, inChannel.size(), outChannel);
    }catch(IOException e){e.printStackTrace();}
    finally
    {
        if (inChannel != null){
        fis.close();
            inChannel.close();
        }
        if (outChannel != null){
        fos.close();
            outChannel.close();
        }
       
    }
}
private int bytearrayToInt(byte[] pwd){
int b=0;
if(pwd!=null)
for(byte y:pwd){
b=b+y;
}
return b;

}

private byte[] getPwd(){
byte[] b=null;
try {
File f=new File(context.getFilesDir(),getName(path));
if(f.exists()){
BufferedReader br=new BufferedReader(new FileReader(f));
String info=br.readLine();
b=info.substring(0,info.lastIndexOf(separator)).getBytes();
br.close();
}
} catch(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return b;

}

private int getBlockSize(){
int size=0;
try {
File f=new File(context.getFilesDir(),getName(path));
if(f.exists()){
BufferedReader br=new BufferedReader(new FileReader(f));
String info=br.readLine();
size=Integer.valueOf(info.substring(info.lastIndexOf(separator)+separator.length()));
br.close();
}
} catch(Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return size;

}

private void saveInfo(String pwd,int blockSize){
try {
String fileName=getName(path);
File f=new File(context.getFilesDir(),fileName);
BufferedWriter bw=new BufferedWriter(new FileWriter(f));
String info=pwd+separator+blockSize;
bw.write(info);
bw.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

private String getName(String path){
return(path.substring(path.lastIndexOf("/")+1));
}



 
}


The constructor of the Locker class will receive the selected file path, password, and context sent from the MainActivity class. These information are used in the locking and unlocking file processes.
The isTextFile method will be called to check whether the selected file is a text file. To improve the performance of locking and unlocking processes, text file and other types files will be encrypted differently .

The lock method locks the selected file by encrypting it so that it is not understandable. If the file is a text file, the two parts (header and footer) of the file will be encrypted. The header part will be 25 percent of file content. The body part is also 25 percent of the file content. The rest is the footer part. The body part will not be encrypted. You can encrypt all content of the file. However, its performance will degrade when the file is large. Another thing that we try to do in improving the locking or unlocking process' performance is using the FileChannel with ByteBuffer to read block of bytes from the source file and write this block to the temporary file. The content of the temporary file will replace the content of the source file. Reading a block of file that contains many bytes at a time and reading one byte at a time are different. Reading each block of the file until all blocks are read is faster than reading one byte one until all content of the file is read. In this locking or unlocking text file process, each block that occupies 25 percent of the file content will be read a a time.

Header (25% or 1/4)
Body (25% or 1/4)
Footer (50% or 1/2)

Encrypting the file content is simple. In this FileLocker app, we encrypt the file content by modifying the byte data of the file. For the part of content to be encrypted, its every byte is added to the sum of bytes generated from the password text.

fos.write(bb.get()+pwdInt);

If the selected file is not a text file, the locking or unlocking process is performed only at the header section of the file. So large audio, video, or image files can be locked or unlocked very fast. The size of header block is 1024 bytes. Each block of the file of this size will be read from the source file that is not the text file.

The unlock method will be called to unlock the locked file by decrypting parts of the file that were encrypted in the locking process. In the unlocking process every byte of each of the file content is subtracted by the sum of bytes previously added to every byte.

fos.write(bb.get()-pwdInt);

The copyFile method is invoked by the lock and unlock method to copy the content of the temparary file to the destination file in file content replacement process.

The bytearrayToInt sums all bytes in the input array of bytes. This method is called to sum all bytes generated from the password text.

The getPwd method converts the password text in to an array of bytes.

The getBlockSize method is invoked by the unlocking process to read the size of the encrypted block from the file that is saved in the locking process. So the unlocking process knows the block of bytes to be read and decrypted. This file stores the password text and the encrypted block size.

The saveInfo method is invoked by the locking process to save the password text and the block size to a file. The password text and the block size is stored in the file in the form as shown below.

password--*****--size

The last method of the Locker class is getName. This method simply returns the file name of the selected file path.

Now we take a look at the MainActivity class that is in the MainActivity.java file.
MainActivity.java file

package com.example.filelocker;
import java.io.File;

import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;

import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends Activity {


   private String path="";
   private String selectedFile="";
   private Context context;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.context=this;
 
    }

    protected void onStart(){
    super.onStart();
    ListView lv=(ListView) findViewById(R.id.files_list);
if(lv!=null){
lv.setSelector(R.drawable.selection_style);
lv.setOnItemClickListener(new ClickListener());
}
path="/mnt";
listDirContents();
    }
 
    public void onBackPressed(){
    if(path.length()>1){ //up one level of directory structure
    File f=new File(path);
    path=f.getParent();
    listDirContents();
    }
    else{
    refreshThumbnails();
    System.exit(0); //exit app
   
    }
    }
 
 
    private void refreshThumbnails(){
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"+ Environment.getExternalStorageDirectory())));
}
    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;
    }

 
    private class ClickListener implements OnItemClickListener{
      public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
      //selected item      
            ViewGroup vg=(ViewGroup)view;
      String selectedItem = ((TextView) vg.findViewById(R.id.label)).getText().toString();
            path=path+"/"+selectedItem;
            //et.setText(path);          
            listDirContents();
      }
     
     
      }
 
 
 
    private void listDirContents(){
    ListView l=(ListView) findViewById(R.id.files_list);
    if(path!=null){
    try{
    File f=new File(path);
    if(f!=null){
    if(f.isDirectory()){
    String[] contents=f.list();
    if(contents.length>0){
    //create the data source for the list
    ListAdapterModel lm=new ListAdapterModel(this,R.layout.listlayout,R.id.label,contents,path);
    //supply the data source to the list so that they are ready to display
    l.setAdapter(lm);
    }
    else
    {
    //keep track the parent directory of empty directory
    path=f.getParent();
    }
    }
    else{
    //capture the selected file path
    selectedFile=path;
    //keep track the parent directory of the selected file
    path=f.getParent();
   
    }
    }
    }catch(Exception e){}
    }    
 
   
    }
 
    public void lockFile(View view){
    EditText txtpwd=(EditText)findViewById(R.id.txt_input);
String pwd=txtpwd.getText().toString();
if(pwd.length()>0){

if(selectedFile.length()>0){
BackTaskLock btlock=new BackTaskLock();
btlock.execute(pwd,null,null);

}
else{
MessageAlert.showAlert("Please a select a file to lock",context);
}
}
else{
MessageAlert.showAlert("Please enter password",context);
}
    }
 
    public void startLock(String pwd){
    Locker locker=new Locker(context,selectedFile,pwd);
locker.lock();
    }

    public void unlockFile(View view){
    EditText txtpwd=(EditText)findViewById(R.id.txt_input);
String pwd=txtpwd.getText().toString();
if(pwd.length()>0){

if(selectedFile.length()>0){

BackTaskUnlock btunlock=new BackTaskUnlock();
btunlock.execute(pwd,null,null);
   
}
else{
MessageAlert.showAlert("Please select a file to unlock",context);
}
}
else{
MessageAlert.showAlert("Please enter password",context);
}

    }
 
    public void startUnlock(String pwd){
    Locker locker=new Locker(context,selectedFile,pwd);
locker.unlock();
    }
 
    private class BackTaskLock extends AsyncTask<String,Void,Void>{
    ProgressDialog pd;
    protected void onPreExecute(){
super.onPreExecute();
//show process dialog
pd = new ProgressDialog(context);
pd.setTitle("Locking the file");
pd.setMessage("Please wait.");
pd.setCancelable(true);
pd.setIndeterminate(true);
pd.show();


}
protected Void doInBackground(String...params){    
try{

startLock(params[0]);

}catch(Exception e){
pd.dismiss();   //close the dialog if error occurs
}
return null;

}
protected void onPostExecute(Void result){
pd.dismiss();
}


}
 
    private class BackTaskUnlock extends AsyncTask<String,Void,Void>{
    ProgressDialog pd;
    protected void onPreExecute(){
super.onPreExecute();
//show process dialog
pd = new ProgressDialog(context);
pd.setTitle("UnLocking the file");
pd.setMessage("Please wait.");
pd.setCancelable(true);
pd.setIndeterminate(true);
pd.show();


}
protected Void doInBackground(String...params){    
try{

startUnlock(params[0]);

}catch(Exception e){
pd.dismiss();   //close the dialog if error occurs
}
return null;

}
protected void onPostExecute(Void result){
pd.dismiss();
}


}

 
}


In the onStart method of MainActivity class, the ListView component is registered to the item click event. Immediately, the listDirContents method is invoked to display files and directories in the /mnt directory. The selection style of the list is defined by the selection_style.xml file.

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 onBackPressed method is invoked every time the user presses the Back button on the  device. It will move back one level of the directory structure until the root directory is reached.

The refreshThumbnails method is invoked in case that the root directory is reached when the user presses the Back button. The external card will be refreshed at that time. This is to make sure that any change that is made to the media files ( audio, audio, or video files) will change the thumbnails of the files.

The lockFile method will be invoked when the user pushes the Lock button. The locking process might take long time. So it is placed in the background by using the AsyncTask class. In the doInBackground method of the AsyncTask class, the lock method of the Locker class is called from the startLock method to perform the locking process. The progress dialog displays to inform the user to wait until the locking process completes.



The unlockFile method is invoked when the user pushes the Unlock button. This process is also placed in background by using the AsyncTask. This method will subsequently invokes the unlock method of the Locker class to unlock the file. The progress dialog displays to inform the user to wait until the unlocking process completes.



When the user touches the Lock or Unlock button without entering the password or selecting the file, the alert dialog displays to inform the user about this action. Since the code to display the dialog is used in different classes (MainActivity and Locker class ), it is defined in a separate class called MessageAlert. Below is the content of the MessageAlert class.

MessageAlert.java file

package com.example.filelocker;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;

public class MessageAlert {
//This method will be invoked to display alert dialog
    public static void showAlert(String message,Context context){
   
    AlertDialog.Builder builder = new AlertDialog.Builder(context);
        builder.setMessage(message);
        builder.setCancelable(true);
        builder.setPositiveButton("OK", new OnClickListener(){
        public void onClick(DialogInterface dialog, int which) {
          dialog.dismiss();
          }

        });
        AlertDialog dialog = builder.create();      
        dialog.show();
   
    }
}


Before running the FileLocker app, you will need to allow it to use the external storage by adding the below code to the AndroidManifest.xml file.

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

Download the apk file of the FileLocker app

nightmode  app Digital clock tdve wallpaper app

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