1

I am trying to develop a android remote control code for playing presentation. The project is divided into two parts:

  1. Socket Server We need to start the server and wait for clients to connect.When the client sends the commands the server processes the command using Robot.KeyPress
  2. Mobile Application (Socket Client) This is an mobile application which sends some commands to the server like "F5".

The current code works fine in android phones with updates upto Marshmallow (tried on Lollipop and Marshmallow) but when tried the same code on Nougat the app is crashing (Android Remote has stopped, Open app again).

Socket Server Code:

    import java.awt.AWTException;
    import java.awt.Robot;
    import java.awt.event.InputEvent;
    import java.awt.event.KeyEvent;
    import java.awt.MouseInfo;
    import java.awt.Point;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.InetSocketAddress;
    import java.net.ServerSocket;
    import java.net.Socket;

    public class RemoteDroidServer {

    private static ServerSocket server = null;
    private static Socket client = null;
    private static BufferedReader in = null;
    private static String line;
    private static boolean isConnected=true;
    private static Robot robot;
    private static final int SERVER_PORT = 8998;

    public static void main(String[] args) {
        boolean leftpressed=false;
        boolean rightpressed=false;

        try{
            robot = new Robot();
            server = new ServerSocket(SERVER_PORT); //Create a server socket on port 8998
            client = server.accept(); //Listens for a connection to be made to this socket and accepts it
            in = new BufferedReader(new InputStreamReader(client.getInputStream())); //the input stream where data will come from client
            System.out.println("Connected to server");
        }catch (IOException e) {
            System.out.println("Error in opening Socket");
            System.exit(-1);
        }catch (AWTException e) {
            System.out.println("Error in creating robot instance");
            System.exit(-1);
        }

        //read input from client while it is connected
        while(isConnected){
            try{
            line = in.readLine(); //read input from client
            System.out.println(line); //print whatever we get from client
            //-----------------VLC Commands--------------------------------
            /*
            Space Bar to Play or Pause. ...
            F to toggle Full Screen. ...
            A to Change Aspect ratio. ...
            Z to Change Zoom Mode. ...
            Ctrl+RightArrow to move forward
            Ctrl+LeftArrow to move backward
            Alt+Left/Alt+Right to Fast Forward slow. ... 
            */

            //if user clicks on fullscreen  (Key- F)
            if(line.equalsIgnoreCase("fullscreen")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_F);
                robot.keyRelease(KeyEvent.VK_F);
            }

            //if user click on Change Aspect ratio (Key- A)
            if(line.equalsIgnoreCase("aspectratio")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_A);
                robot.keyRelease(KeyEvent.VK_A);
            }

            //if user clicks on Increase Volume (CTRL + UpArrowKey)
            if(line.equalsIgnoreCase("volplus")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_CONTROL);
                robot.keyPress(KeyEvent.VK_UP);
                // CTRL+UpArrowKey is now pressed (receiving application should see a "key down" event.)
                robot.keyRelease(KeyEvent.VK_UP);
                robot.keyRelease(KeyEvent.VK_CONTROL);
            }

            //if user clicks on Decrease Volume (CTRL + DownArrowKey)
            if(line.equalsIgnoreCase("volminus")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_CONTROL);
                robot.keyPress(KeyEvent.VK_DOWN);
                // CTRL+DownArrowKey is now pressed (receiving application should see a "key down" event.)
                robot.keyRelease(KeyEvent.VK_DOWN);
                robot.keyRelease(KeyEvent.VK_CONTROL);
            }

            //if user clicks on move forward (CTRL + RightArrow)
            if(line.equalsIgnoreCase("forward")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_CONTROL);
                robot.keyPress(KeyEvent.VK_RIGHT);
                // CTRL+UpArrowKey is now pressed (receiving application should see a "key down" event.)
                robot.keyRelease(KeyEvent.VK_RIGHT);
                robot.keyRelease(KeyEvent.VK_CONTROL);
            }
            //if user clicks on move forward (CTRL + LeftArrow)
            if(line.equalsIgnoreCase("backward")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_CONTROL);
                robot.keyPress(KeyEvent.VK_LEFT);
                // CTRL+UpArrowKey is now pressed (receiving application should see a "key down" event.)
                robot.keyRelease(KeyEvent.VK_RIGHT);
                robot.keyRelease(KeyEvent.VK_LEFT);
            }

            //-------------------------------------------------------------
            //if user clicks on f5
            if(line.equalsIgnoreCase("F5")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_F5);
                robot.keyRelease(KeyEvent.VK_F5);
            }

            //if user clicks on esc
            if(line.equalsIgnoreCase("escape")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_ESCAPE);
                robot.keyRelease(KeyEvent.VK_ESCAPE);
            }

            //if user clicks on first slide
            if(line.equalsIgnoreCase("home")){
                //Simulate press and release of key 'Home'
                robot.keyPress(KeyEvent.VK_HOME);
                robot.keyRelease(KeyEvent.VK_HOME);
            }

            //if user clicks on last slide
            if(line.equalsIgnoreCase("end")){
                //Simulate press and release of key 'END'
                robot.keyPress(KeyEvent.VK_END);
                robot.keyRelease(KeyEvent.VK_END);
            }
            //----------------------------------------------------------------------------          
            //if user clicks on next
            if(line.equalsIgnoreCase("next")){
                //Simulate press and release of key 'n'
                robot.keyPress(KeyEvent.VK_N);
                robot.keyRelease(KeyEvent.VK_N);
            }

            //if user clicks on previous
            else if(line.equalsIgnoreCase("previous")){
                //Simulate press and release of key 'p'
                robot.keyPress(KeyEvent.VK_P);
                robot.keyRelease(KeyEvent.VK_P);                    
            }
            //if user clicks on play/pause
            else if(line.equalsIgnoreCase("play")){
                //Simulate press and release of spacebar
                robot.keyPress(KeyEvent.VK_SPACE);
                robot.keyRelease(KeyEvent.VK_SPACE);
            }
            //input will come in x,y format if user moves mouse on mousepad
            else if(line.contains(",")){
                float movex=Float.parseFloat(line.split(",")[0]);//extract movement in x direction
                float movey=Float.parseFloat(line.split(",")[1]);//extract movement in y direction
                Point point = MouseInfo.getPointerInfo().getLocation(); //Get current mouse position
                float nowx=point.x;
                float nowy=point.y;
                robot.mouseMove((int)(nowx+movex),(int)(nowy+movey));//Move mouse pointer to new location
            }
            //if user taps on mousepad to simulate a left click
            else if(line.contains("left_click")){
                //Simulate press and release of mouse button 1(makes sure correct button is pressed based on user's dexterity)
                robot.mousePress(InputEvent.BUTTON1_DOWN_MASK);
                robot.mouseRelease(InputEvent.BUTTON1_DOWN_MASK);
            }
            //Exit if user ends the connection
            else if(line.equalsIgnoreCase("exit")){
                isConnected=false;
                //Close server and client socket
                server.close();
                client.close();
            }
            } catch (IOException e) {
                System.out.println("Read failed");
                System.exit(-1);
            }
        }
    }
}

My Android Code is :

import android.content.Context;
import android.content.DialogInterface;
import android.os.AsyncTask;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

public class Powerpoint extends AppCompatActivity implements View.OnClickListener{

    Context context;
    Button esc;
    Button nextButton;
    Button previousButton;
    Button end;
    Button btnConnect;
    Button f5;
    Button home;

    private boolean isConnected=false;
    private boolean mouseMoved=false;
    private Socket socket;
    private PrintWriter out;
    private String serverIP= Constants.SERVER_IP;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_powerpoint);

        context = this; //save the context to show Toast messages

        //Get references of all buttons
        esc = (Button)findViewById(R.id.btnEnd);
        nextButton = (Button)findViewById(R.id.btnNext);
        previousButton = (Button)findViewById(R.id.btnPrevious);
        f5 = (Button)findViewById(R.id.btnStart);
        btnConnect = (Button)findViewById(R.id.btnConnect);
        home = (Button)findViewById(R.id.btnHome);
        end = (Button)findViewById(R.id.btnLastSlide);

        //this activity extends View.OnClickListener, set this as onClickListener
        //for all buttons
        esc.setOnClickListener(this);
        nextButton.setOnClickListener(this);
        previousButton.setOnClickListener(this);
        f5.setOnClickListener(this);
        home.setOnClickListener(this);
        end.setOnClickListener(this);

        btnConnect.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // get prompts.xml view
                LayoutInflater li = LayoutInflater.from(context);
                View promptsView = li.inflate(R.layout.prompt, null);

                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
                        context);

                // set prompts.xml to alertdialog builder
                alertDialogBuilder.setView(promptsView);

                final EditText userInput = (EditText) promptsView
                        .findViewById(R.id.editTextDialogUserInput);

                // set dialog message
                        alertDialogBuilder
                .setCancelable(false)
                        .setPositiveButton("OK",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog,int id) {
                                        // get user input and set it to result
                                        // edit text
                                        serverIP=userInput.getText().toString();
                                        Toast.makeText(getApplicationContext(),"Ip:"+serverIP,Toast.LENGTH_SHORT).show();
                                        ConnectPhoneTask connectPhoneTask = new ConnectPhoneTask();
                                        connectPhoneTask.execute(serverIP); //try to connect to server in another thread

                                    }
                                })
                        .setNegativeButton("Cancel",
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog,int id) {
                                        dialog.cancel();
                                    }
                                });

                // create alert dialog
                AlertDialog alertDialog = alertDialogBuilder.create();

                // show it
                alertDialog.show();


            }
        });

    }

    //OnClick method is called when any of the buttons are pressed
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btnEnd:
                if (isConnected && out!=null) {
                    out.println(Constants.ESCAPE);//send "escape" to server
                }

                break;
            case R.id.btnNext:
                if (isConnected && out!=null) {
                    out.println(Constants.NEXT); //send "next" to server
                }
                break;
            case R.id.btnPrevious:
                if (isConnected && out!=null) {
                    out.println(Constants.PREVIOUS); //send "previous" to server
                }
                break;

            case R.id.btnStart:
                if (isConnected && out!=null) {
                    out.println(Constants.F5); //send "previous" to server
                }
                break;
            case R.id.btnHome:
                if (isConnected && out!=null) {
                    out.println(Constants.HOME); //send "home" to server
                }
                break;
             case R.id.btnLastSlide:
                if (isConnected && out!=null) {
                    out.println(Constants.END); //send "end" to server
                }
                break;
        }

    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        if(isConnected && out!=null) {
            try {
                out.println("exit"); //tell server to exit
                socket.close(); //close socket
            } catch (IOException e) {
                Log.e("remotedroid", "Error in closing socket", e);
            }
        }
    }

    public class ConnectPhoneTask extends AsyncTask<String,Void,Boolean> {

        @Override
        protected Boolean doInBackground(String... params) {
            boolean result = true;
            try {
                InetAddress serverAddr = InetAddress.getByName(params[0]);
                socket = new Socket(serverAddr, Constants.SERVER_PORT);//Open socket on server IP and port
                isConnected=true;
            } catch (IOException e) {
                Log.e("remotedroid", "Error while connecting", e);
                result = false;
                isConnected=false;
            }
            return result;
        }

        @Override
        protected void onPostExecute(Boolean result)
        {
            isConnected = result;
            Toast.makeText(context, isConnected ? "Connected to server!" : "Error while connecting", Toast.LENGTH_LONG).show();
            try {
                if(isConnected) {
                    out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket
                            .getOutputStream())), true); //create output stream to send data to server
                }
            }catch (IOException e){
                Log.e("remotedroid", "Error while creating OutWriter", e);
                Toast.makeText(context,"Error while connecting",Toast.LENGTH_LONG).show();
            }
        }
    }

}

Constants Class code:

public class Constants {
    public static final String SERVER_IP = "192.168.1.5";
    public static final int SERVER_PORT = 8998;
    public static final String NEXT="next";
    public static final String PREVIOUS="previous";
    public static final String ESCAPE="escape";
    public static final String F5="f5";
    public static final String HOME="home";
    public static final String END="end";

    public static final String SPACE="play";
    public static final String VOLUP="volplus";
    public static final String VOLDOWN="volminus";
    public static final String FASTFORWARD="forward";
    public static final String BACKWARD="backward";
    public static final String ZOOM="fullscreen";
    public static final String ASPECTRATIO="aspectratio";
}

Layout File:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_powerpoint"
    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="com.sahajsolns.socketprogrammingdemo.Powerpoint">


    <Button
        android:text="Start PPT"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginTop="48dp"
        android:id="@+id/btnStart"
        android:layout_below="@+id/lblModuleTitle"
        android:layout_centerHorizontal="true" />

    <TextView
        android:text="Powerpoint Module"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="45dp"
        android:id="@+id/lblModuleTitle"
        android:textSize="18sp"
        android:textColor="@android:color/holo_orange_dark"
        android:textStyle="normal|bold"
        android:lineSpacingExtra="8sp" />

    <Button
        android:text="Next Slide"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/btnNext"
        android:layout_below="@+id/btnStart"
        android:layout_toRightOf="@+id/btnStart"
        android:layout_toEndOf="@+id/btnStart" />

    <Button
        android:text="End PPT"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/btnEnd"
        android:layout_below="@+id/btnNext"
        android:layout_toRightOf="@+id/btnPrevious"
        android:layout_toEndOf="@+id/btnPrevious" />

    <Button
        android:text="Previous Slide"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/btnPrevious"
        android:layout_below="@+id/btnStart"
        android:layout_toLeftOf="@+id/btnStart"
        android:layout_toStartOf="@+id/btnStart" />

    <Button
        android:text="Connect To Server"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/btnConnect" />

    <Button
        android:text="Home"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_below="@+id/btnPrevious"
        android:layout_toLeftOf="@+id/btnEnd"
        android:layout_toStartOf="@+id/btnEnd"
        android:id="@+id/btnHome" />

    <Button
        android:text="Last Slide"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/btnLastSlide"
        android:layout_below="@+id/btnNext"
        android:layout_toRightOf="@+id/btnEnd"
        android:layout_toEndOf="@+id/btnEnd" />

</RelativeLayout>
Cœur
  • 37,241
  • 25
  • 195
  • 267
Sushant S
  • 33
  • 5
  • What was the stack trace of the crash? – Gabe Sechan Mar 24 '17 at 05:46
  • 03-24 20:58:09.922 10821-10821/com.sahajsolns.socketprogrammingdemo E/AndroidRuntime: FATAL EXCEPTION: main Process: com.sahajsolns.socketprogrammingdemo, PID: 10821 android.os.NetworkOnMainThreadException at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1317) at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111) at java.net.SocketOutputStream.write(SocketOutputStream.java:157) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291) – Sushant S Mar 24 '17 at 15:41
  • at sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:295) at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:141) at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229) at java.io.BufferedWriter.flush(BufferedWriter.java:254) at java.io.PrintWriter.newLine(PrintWriter.java:482) at java.io.PrintWriter.println(PrintWriter.java:629) at java.io.PrintWriter.println(PrintWriter.java:740) at com.sahajsolns.socketprogrammingdemo.Powerpoint.onClick(Powerpoint.java:138) – Sushant S Mar 24 '17 at 15:41
  • at android.view.View.performClick(View.java:5612) at android.view.View$PerformClick.run(View.java:22285) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6123) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757) – Sushant S Mar 24 '17 at 15:42
  • http://stackoverflow.com/questions/6343166/android-os-networkonmainthreadexception – Gabe Sechan Mar 24 '17 at 15:48
  • 1
    @Gabe i am executing the thread in Async Task itself. The same code works fine till marshmellow but gives an error in Nougat – Sushant S Mar 25 '17 at 03:46
  • Look at your on click. You're writing to the network there – Gabe Sechan Mar 25 '17 at 03:48
  • The printwriter out is connected to the socket. So calling println on it tries to write to the network on the main thread – Gabe Sechan Mar 25 '17 at 03:50
  • 1
    @gabe can you please help me out to rectify the same. Ty – Sushant S Mar 25 '17 at 04:07
  • i am also facing the same issue is there any solution for this.. – Yogesh Borhade Oct 31 '18 at 05:25

0 Answers0