Transfer Files over local WiFi network using Android TCP connection

2
234

Transfer Files over local WiFi network using Android TCP connection | In this tutorial, we will learn how to how to transfer files over a local WiFi network by socket programming in Android (An Application like Xender or Share it!). We’ll use TCP (Transmission Control Protocol) to establish a connection between two or more Android mobile phones. Before reading further,  you can read about android TCP connection from this answer on StackOverflow.

In this tutorial, we’ll create a sample application which can first establish a connection with peer devices and then the host can select and send files over the established connection.

Output:

Download Sample App: File_1.0 [CLICK HERE]

Download Sample Project:  File [CLICK HERE]

Wants to Learn Advanced Android Application development from scratch- Beyond Basics

Creating a New Project:

Open your Android Studio & create a new Project, give name whatever you like and We’ll keep all the things by default and clicked finish.

Adding permissions to manifest.xml:

For selecting files from the device and accessing WiFi, we need following four permissions from user:

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

So add the above permission into your manifest.xml file. After adding it to manifest, It should look like something as following:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="kaushal28.file">
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

    </application>

</manifest>

 

Now in MainActivity.java file, add the following code:

package kaushal28.file;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.support.v7.app.ActionBar;
import android.text.InputType;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends Activity
{
    private Button serverTransmitButton;
    private Button clientReceiveButton;
    private Button serverUDPButton;
    private Button clientUDPButton;
    private int PICKFILE_REQUEST_CODE = 100;
    private String filePath="";
    private String wholePath="";
    private Button changeName;
    private String m_Text = "";

    private int ASK_MULTIPLE_PERMISSION_REQUEST_CODE = 1;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (android.os.Build.VERSION.SDK_INT >= 23) {
            requestPermissions(new String[]{
                            Manifest.permission.INTERNET,
                            Manifest.permission.READ_EXTERNAL_STORAGE,
                            Manifest.permission.WRITE_EXTERNAL_STORAGE,
                            Manifest.permission.ACCESS_NETWORK_STATE},
                    ASK_MULTIPLE_PERMISSION_REQUEST_CODE);
        }

        changeName = (Button)findViewById(R.id.change);


        changeName.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showInputDialog();
            }
        });

        // TCP
        serverTransmitButton = (Button) findViewById(R.id.button_TCP_server);
        serverTransmitButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Log.i("Start Server Clicked", "yipee");

//////////////////////////////////////////////

                //open a file manager to let user choose desired file.
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.setType("file/*");
                startActivityForResult(intent, PICKFILE_REQUEST_CODE);



            }
        });


        clientReceiveButton = (Button) findViewById(R.id.button_TCP_client);
        clientReceiveButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Log.i("Read Button Clicked", "yipee");
                startService(new Intent(MainActivity.this, NameService.class));
                second s = new second(MainActivity.this,MainActivity.this);
                s.execute();

            }
        });
    }

    private void showInputDialog() {

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("Title");

// Set up the input
        final EditText input = new EditText(this);
// Specify the type of input expected; this, for example, sets the input as a password, and will mask the text
        input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_CLASS_TEXT);
        builder.setView(input);

// Set up the buttons
        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                m_Text = input.getText().toString();
                PreferenceManager.getDefaultSharedPreferences(MainActivity.this).edit().putString("name", m_Text).commit();
                Toast.makeText(MainActivity.this,m_Text,Toast.LENGTH_LONG).show();
            }
        });
        builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });

        builder.show();

    }

    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//        filePath = data.getDataString();

            if(requestCode == Activity.RESULT_CANCELED){

            }
             else{
                Uri uri = data.getData();
                String uriString = uri.toString();
                File myFile = new File(uriString);
                String path = myFile.getAbsolutePath();

                if (uriString.startsWith("content://")) {
                    Cursor cursor = null;
                    try {
                        cursor = this.getContentResolver().query(uri, null, null, null, null);
                        if (cursor != null && cursor.moveToFirst()) {
                            filePath = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
                            Toast.makeText(this,filePath,Toast.LENGTH_LONG).show();
                        }
                    }
                    catch (Exception e){
                        e.printStackTrace();
                    }
                } else if (uriString.startsWith("file://")) {
                    filePath = myFile.getName();
                    Toast.makeText(this,filePath,Toast.LENGTH_LONG).show();
                }


                Uri Selected = data.getData();

                wholePath = getRealPathFromURI(Selected);

                Toast.makeText(this,wholePath,Toast.LENGTH_LONG).show();


                first f = new first(MainActivity.this,MainActivity.this,filePath,wholePath);
                f.execute();
            }





        //TODO handle your request here
        super.onActivityResult(requestCode, resultCode, data);
    }


    private String getRealPathFromURI(Uri contentURI) {
        String result;
        Cursor cursor = getContentResolver().query(contentURI, null, null, null, null);
        if (cursor == null) { // Source is Dropbox or other similar local file path
            result = contentURI.getPath();
        } else {
            cursor.moveToFirst();
            int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
            result = cursor.getString(idx);
            cursor.close();
        }
        return result;
    }

}

 

This code handles simple button clicks and files selecting operation. When you ‘ll press send, it will start an intent to a file manager application to open a file picker dialogue. So make sure in your device a file manager is installed, otherwise, this will give an error.

Layout file for Main activity is given as following: activity_main.xml

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

    <Button android:id="@+id/button_TCP_server"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Send" />

    <Button android:id="@+id/button_TCP_client"
        android:layout_width="fill_parent"
        android:layout_below="@+id/button_TCP_server"
        android:layout_height="wrap_content"
        android:text="Receive(Wait)" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="If You select SEND, then available devices will appear below, Select a valid One:"
        android:textStyle="bold"
        android:id="@+id/tv"
        android:layout_below="@+id/button_TCP_client"/>

    <ListView
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:id="@+id/listView"
        android:clickable="true"
        android:layout_below="@+id/tv"
        android:layout_alignParentStart="true" />

    <Button
        android:text="change Name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/listView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="34dp"
        android:id="@+id/change" />


</RelativeLayout>

This layout is basic and only for demonstration purpose, You can create a beautiful UI for this application.

Now, here is the main part. When you click “Send” button from main activity, The following file will run. It contains network operations (We’ll see these operations later in this post), and Android doesn’t allow any network operations in the main thread(UI thread). So here for these operations, we are using Async Task class to do these network operations in a background thread.  For an understanding of AsyncTask class, You can refer the standard Android Documentation from here.

first.java

package kaushal28.file;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.ArrayList;

/**
 * Created by kaushal28 on 13/12/16.
 */

public class first extends AsyncTask<Void,Void,Void> {

    private ArrayList<String> a;
    private ListView listView;
    private Context context;
    private Activity activity;
    private String destinationAddress="-1";
    private String filePath;
    private String wholePath;
    private boolean xceptionFlag = false;
    private Socket socket;
    private String hostName,canonicalHostname;
    private String givenName;

    first(Context context, Activity act, String path, String fullPath){
        this.context = context;
        this.activity = act;
        this.filePath = path;
        this.wholePath = fullPath;
    }

    @Override
    protected void onProgressUpdate(Void... values) {
        super.onProgressUpdate(values);



        //Setting listView to display IP Addresses Which are available to send our files!
        listView = (ListView)activity.findViewById(R.id.listView);
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(
                context,
                android.R.layout.simple_list_item_1,
                a );



        listView.setAdapter(arrayAdapter);

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {

                //now we have host names like Xender so change following line. Get Ipaddress instead of host names.
                destinationAddress = (String)adapterView.getItemAtPosition(position);

            }
        });


    }

    @Override
    protected Void doInBackground(Void... voids) {

        System.out.println("array list");
        ArrayList<File> files = new ArrayList<>();
        System.out.println("about to create.");

        //Add files (Music or whatever) to array list to send them!
//        files.add(new File("mnt/sdcard/Download/ab.mp3"));
//        files.add(new File("mnt/sdcard/Download/bh.mp3"));
//        files.add(new File("mnt/sdcard/Download/bc.mp3"));

       // filePath = filePath.replace("%20"," ");

        files.add(new File(wholePath));
        System.out.println("file created..");
        try {


            //Receiving IP addresses which are available to send our files(Music)!!
            a = getClientList();


            //update the UI to display the received IP addresses!!
            publishProgress();


            //busy waiting for user to select appropriate IP address to send files!
            while (destinationAddress.equals("-1")){

            }

            //User has selected something, It's time to send files there!
            socket = new Socket(destinationAddress,5004);
//            socket.setReuseAddress(true);

            System.out.println("Connecting...");
            DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
            DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
            System.out.println(files.size());
            //write the number of files to the server
            dos.writeInt(files.size());
            dos.flush();


            //write file size
            for(int i = 0;i< files.size();i++){
                int file_size = Integer.parseInt(String.valueOf(files.get(i).length()));
                dos.writeLong(file_size);
                dos.flush();
            }

            //write file names
            for(int i = 0 ; i < files.size();i++){
                dos.writeUTF(files.get(i).getName());
                dos.flush();
            }

            //buffer for file writing, to declare inside or outside loop?
            int n = 0;
            byte[]buf = new byte[4092];
            //outer loop, executes one for each file
            for(int i =0; i < files.size(); i++){

                System.out.println(files.get(i).getName());
                //create new fileinputstream for each file
                FileInputStream fis = new FileInputStream(files.get(i));

                //write file to dos
                while((n =fis.read(buf)) != -1){
                    dos.write(buf,0,n);
                    dos.flush();

                }
                //should i close the dataoutputstream here and make a new one each time?
            }
            //or is this good?
            dos.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            xceptionFlag = true;
            e.printStackTrace();
        }

        Log.i("===end of start ====", "==");
        try{
            if(socket!=null && !socket.isClosed()){
                socket.close();
            }
        }
        catch (Exception e){
            xceptionFlag = true;
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        if(xceptionFlag){
            Toast.makeText(context,"Something went wrong.",Toast.LENGTH_LONG).show();
        }
        else{
            Toast.makeText(context,"files Sent Successfully!!",Toast.LENGTH_LONG).show();
        }
    }

    public ArrayList<String> getClientList() {

        final ArrayList<String> arr = new ArrayList<>(25);

        Thread thread = new Thread(new Runnable() {



            @Override
            public void run() {
                BufferedReader br = null;
                boolean isFirstLine = true;

                try {
                    br = new BufferedReader(new FileReader("/proc/net/arp"));
                    String line;

                    while ((line = br.readLine()) != null) {
                        if (isFirstLine) {
                            isFirstLine = false;
                            continue;
                        }

                        String[] splitted = line.split(" +");

                        if (splitted != null && splitted.length >= 4) {

                            String ipAddress = splitted[0];
                            String macAddress = splitted[3];

                            boolean isReachable = InetAddress.getByName(
                                    splitted[0]).isReachable(500); 
 // this is network call so we cant do that on UI thread, so i(kaushal28) take background thread.
                            if (isReachable) {
                                Log.d("Device Information", ipAddress + " : "
                                        + macAddress);

                                //added afterwards for receiving names of available clients..
                                //but by adding this names to array list, the ip addresses is lost. so do something.
                                try {
                                    Socket socket = new Socket();
                                    //receive from port 5006 and timeout is 5s.
                                    socket.connect(new InetSocketAddress(ipAddress, 5006), 5000);
                                    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                                    givenName = reader.readLine();
                                    reader.close();
                                    socket.close();
                                    Log.i("TAG", givenName);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                                //Assigning values to final array or array list is perfectly fine.
                               
                                arr.add(ipAddress);
                                InetAddress inetAddress = InetAddress.getByName(ipAddress);
                                hostName = inetAddress.getHostName();
                                canonicalHostname = inetAddress.getCanonicalHostName();

                              //  Toast.makeText(context,hostName+canonicalHostname,Toast.LENGTH_LONG).show();

                            }

                        }

                    }

                } catch (Exception e) {
                    xceptionFlag = true;
                    e.printStackTrace();
                } finally {
                    try {
                        br.close();
                    } catch (IOException e) {
                        xceptionFlag = true;
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();

        //Wait util thread is completed. And then return array. 
         Otherwise it'll return null array or array list or what ever.
        try{
            thread.join();
        }
        catch (Exception e){
            xceptionFlag = true;
            e.printStackTrace();
        }
        return arr;

    }
}

This class first of all call the user-defined method named “getClientList()” which will iterate over all connected devices and check whether they are reachable or not. If the device is reachable, it will add it’s IP address for later displaying purpose. These all operations are done in a background thread as described previously. You can observe the override method named “doInBackGround()”, which is a separate thread other than UI thread.

Now after getting all the client list as described above, the list view in main activity is updated by the method named “onPostExecute()”. NOTE: Whatever code you write in this method is considered as written in main UI thread.

Now, when the list of all clients is available, this will start a new socket on port number 5004.

Related:

Enable/Disable Wi-Fi programatically in Android Wifi connect

Uploading file to Google Drive in Android Application

How to read PDF files in android

Play a music file in background using services

Android Pick File Programatically

Here comes the role of receiver class. Code for it is given below:

second.java

package kaushal28.file;

import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;

/**
 * Created by kaushal28 on 13/12/16.
 */

public class second extends AsyncTask<Void,Void,Void> {


    private Context context;
    private Activity activity;
    private boolean xceptionFlag = false;
    private ServerSocket ss;

    second(Context c, Activity a){
        this.context = c;
        this.activity = a;

    }

    @Override
    protected Void doInBackground(Void... voids) {


        try {

//            ServerSocket ss = new ServerSocket(5004);

            //this is done instead of above line because it was giving error of address is already in use.
            ss = new ServerSocket();
            ss.setReuseAddress(true);
            ss.bind(new InetSocketAddress(5004));

            System.out.println("waiting");

            Socket socket = ss.accept();
            System.out.println("Accepted!");
            DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
            DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
//read the number of files from the client
            int number = dis.readInt();
            ArrayList<File>files = new ArrayList<>(number);
            System.out.println("Number of Files to be received: " +number);

            ArrayList<Long> fileSize = new ArrayList<>(number);


            for(int i = 0; i < number ;i++){
                long size = dis.readLong();
                System.out.println(size);
                fileSize.add(size);
            }

            //read file names, add files to arraylist
            for(int i = 0; i< number;i++){
                File file = new File(dis.readUTF());
                files.add(file);
            }
            int n = 0;
            byte[]buf = new byte[4092];

            //outer loop, executes one for each file
            for(int i = 0; i < files.size();i++){

                System.out.println("Receiving file: " + files.get(i).getName());

                //Create new Folder for our app, if it is not there and store received files there in our separate folder.
                File folder = new File(Environment.getExternalStorageDirectory() +
                        File.separator + "File");
                boolean success = true;
                if (!folder.exists()) {
                    success = folder.mkdirs();
                }
                if (success) {
                    // Do something on success
                } else {
                    // Do something else on failure
                }


                //create a new fileoutputstream for each new file
                FileOutputStream fos = new FileOutputStream("mnt/sdcard/File/" +files.get(i).getName());
                //read file

                while (fileSize.get(i) > 0 && (n = dis.read(buf, 0, (int)Math.min(buf.length, fileSize.get(i)))) != -1)
                {
                    fos.write(buf,0,n);
                    long x = fileSize.get(i);
                    x = x-n;
                    fileSize.set(i,x);
                }
                fos.close();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            xceptionFlag = true;
            e.printStackTrace();

        }
        ////////////////////
        Log.i("== the end of read ====", "==");
        try{
            if(!ss.isClosed()){
                ss.close();
            }
        }
        catch (Exception e){
            xceptionFlag = true;
            e.printStackTrace();
        }
        return null;
    }


    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        if (!xceptionFlag){
            Toast.makeText(context,"files Received Successfully!!",Toast.LENGTH_LONG).show();
        }
        else{
            Toast.makeText(context,"Something went wrong.",Toast.LENGTH_LONG).show();
        }

    }
}

This class also does network operations and therefore uses AsyncTask. First of all, when used presses “Receive” button, It will wait for incoming connection on port 5004(Note that port used here and in sender class must be same otherwise the connection will not be established). When it detects any incoming connection, It will accept it and read the incoming data(here our file) and will store it in the internal storage of your device in the folder named “File”. You can change these things from the above code.

Notes:

In order to run the above code or the sample application, install the app on two or more devices. Then in one device press “Receive” and then in other device press “Send” and select any file from the device.(This order of pressing Receive and Send must be maintained because you can not establish a connection before start waiting for the same.) After you select the file, IP address of connected device will appear, click on it and the file will be transferred. To verify the transfer, check the folder named “File” in your internal storage. If You are Beginner Learn Android application development from the scratch


If you really liked the article, please subscribe to our YouTube Channel for videos related to this article.Please find us on Twitter and Facebook.

2 COMMENTS

  1. hey, I have error with this code. In the line of “startService(new Intent(MainActivity.this, NameService.class)); ” with NameService class. the error is “Cannot resolve Symbol ‘NameService’.
    Please give me a solution

  2. hey, I have error with this code. In the line of “startService(new Intent(MainActivity.this, NameService.class)); ” of MAinActivity.java class with NameService class. the error is “Cannot resolve Symbol ‘NameService’.
    Please give me a solution

LEAVE A REPLY

Please enter your comment!
Please enter your name here