Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to publish device name (Variable) over WiFi, as some File transfer applications do?

I want to publish or advertise my device name over WiFi, which is variable and can be changed by user.

For example, Take a file transfer application Xender. We can see the device name set by users on screen when we select receive option in the app. Here is screen shot.

enter image description here

You can see in the image that name shah.kaushal is appearing.

I've searched many results on Internet, but can't figure out what the exact thing it is.

I know about host name but I think generally it is not changed by such apps and I think it need some special privileges on android to do so. So I'm sure it is not the host name, Which we can get easily from IP address.

Please note I'm not copying any other apps features. I want this in my music player application to share songs.

To do so, I've used TCP connection between devices. And I can successfully send songs from one device to another. But It requires IP address of the device. Which is not user friendly.

Here is the screenshot of my basic music sharing activity, Which lists the available IP addresses and user have to select one IP from the list.

enter image description here

Here instead of IP addresses, I want to display device names.

My code for sending file is:

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


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

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

        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();

            }

        }

        dos.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        xceptionFlag = true;
        e.printStackTrace();
    }

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

    return null;
}

And code for receiving file is:

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


    try {


        //this is done isntead of above line because it was givind 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<File>(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;
}

I've included the code for reference. Thanks.

like image 747
Kaushal28 Avatar asked Dec 15 '16 12:12

Kaushal28


1 Answers

Just have the name set and stored in SharedPreferences or wherever as a String, then when you show the screen where you are listing the IP, connect to every one of them and transfer this String and display it instead of the IP. Something like Send a string instead of byte through socket in Java to transfer the String.

When you want to publish the name from a device start this service, and stop it when it shouldn't be publishing the name anymore:

public class NameService extends Service {

    private volatile boolean running;
    private volatile String myName;
    private volatile ServerSocket serverSocket;

    public NameService() {
    }

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            serverSocket = new ServerSocket();
            serverSocket.bind(new InetSocketAddress(5006));
            serverSocket.setReuseAddress(true);
            serverSocket.setSoTimeout(2000);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        myName = PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
                .getString("NAME_STRING", "TEST.NAME");
        if (!running)
        {
            running = true;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (running)
                    {
                        try {
                            Socket socket = serverSocket.accept();
                            PrintWriter writer = new PrintWriter(new BufferedWriter(
                                    new OutputStreamWriter(socket.getOutputStream())),
                                    true);
                            writer.println(myName);

                            writer.close();
                            socket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }

                    }
                }
            }).start();
        }
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        running = false;
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

Then when want to display the list of receivers, connect to each one and do the following:

try {
        Socket socket = new Socket();
        socket.connect(new InetSocketAddress(ipAddress, 5006), 5000);
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String message = reader.readLine();
        reader.close();
        socket.close();
        Log.i("TAG", message);
        } catch (IOException e) {
        e.printStackTrace();
     }

You can do further calls to startService() if you want to change the name when it is running.

I recommend you use Service or IntentService for your file transfers, AsyncTask is not appropriate.

like image 159
Steve M Avatar answered Oct 23 '22 23:10

Steve M