Android Widget TCP Client developed for connecting with server app and sending commands to:
- Restart the computer
- Turn Off the computer
- Hibernate the computer
If You see any issues, please inform! Every bit of help is very welcome.
-
In MainActivity we have two overriden methods: onUpdate and onReceive.
- In onUpdate we manage the views and intents. We are setting the pending intents to buttons and update via AppWidgetManager. Look at this code:
@Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); for(int i=0; i<appWidgetIds.length; i++){ int currentId = appWidgetIds[i]; views = new RemoteViews(context.getPackageName(), R.layout.activity_main); //Creating intents and pending intents for onReceive method Intent shutdownIntent = new Intent(context, MainActivity.class) ; shutdownIntent.setAction(ACTION_SHUTDOWN) ; PendingIntent pendingShutdown = PendingIntent.getBroadcast(context, 0, shutdownIntent, 0) ; Intent restartIntent = new Intent(context, MainActivity.class) ; restartIntent.setAction(ACTION_RESTART) ; PendingIntent pendingRestart = PendingIntent.getBroadcast(context, 1 , restartIntent, 0) ; Intent hibernateIntent = new Intent(context, MainActivity.class) ; hibernateIntent.setAction(ACTION_HIBERNATE) ; PendingIntent pendingHibernate = PendingIntent.getBroadcast(context, 2, hibernateIntent, 0); //setting pending intents to specific buttons views.setOnClickPendingIntent(R.id.shutdownButton , pendingShutdown) ; views.setOnClickPendingIntent(R.id.restartButton , pendingRestart) ; views.setOnClickPendingIntent(R.id.hibernateButton, pendingHibernate); appWidgetManager.updateAppWidget(currentId, views); } }
The
ACTION_SHUTDOWN
,ACTION_RESTART
andACTION_HIBERNATE
are static Strings with names of the actions from AndroidManifest:<receiver android:name="io.panwrona.turnmeoff.MainActivity" > <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="io.panwrona.turnmeoff.ACTION_SHUTDOWN" /> <action android:name="io.panwrona.turnmeoff.ACTION_RESTART"/> <action android:name="io.panwrona.turnmeoff.ACTION_HIBERNATE"/> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/app_widget" /> </receiver> ``` *In onReceive method we are managing button clicking, which creates AsyncTask object. ```java @Override public void onReceive(Context context, Intent intent){ super.onReceive(context,intent); views = new RemoteViews(context.getPackageName(), R.layout.activity_main); widget = new ComponentName(context, MainActivity.class); awManager = AppWidgetManager.getInstance(context); /** * Checking and comparing actions delivered from button clicks and managing ones. * If button is clicked and everything is going well, specific AsyncTask is being created, * and rest of procedure is being done. */ if(intent.getAction().equals(ACTION_SHUTDOWN)){ new ShutdownAsyncTask(getmHandler(context)).execute(""); }else if(intent.getAction().equals(ACTION_RESTART)){ new RestartAsyncTask(getmHandler(context)).execute(""); }else if(intent.getAction().equals(ACTION_HIBERNATE)){ new HibernateAsyncTask(getmHandler(context)).execute(""); } } ```
-
Let's say You clicked 'Shutdown PC' button. This is what happens:
- The TCPClient object is created in ShutdownAsyncTask class:
/** * Overriden method from AsyncTask class. There the TCPClient object is created. * @param params From MainActivity class empty string is passed. * @return TCPClient object for closing it in onPostExecute method. */ @Override protected TCPClient doInBackground(String... params) { Log.d(TAG, "In do in background"); try{ tcpClient = new TCPClient(mHandler, COMMAND, new IpGetter().getIp(), new TCPClient.MessageCallback() { @Override public void callbackMessageReceiver(String message) { publishProgress(message); } }); }catch (NullPointerException e){ Log.d(TAG, "Caught null pointer exception"); e.printStackTrace(); } tcpClient.run(); return null; }
The IpGetter class is dynamically searching for the open server socket on your local network, on the 'hardcoded' port. Take a look on that class, there is everything explained in comments.
- After creating the object and getting the proper ip number, the TCPClient object is trying to create socket, PrintWriter object and BufferReader for input/output.
public void run() { mRun = true; try { // Creating InetAddress object from ipNumber passed via constructor from IpGetter class. InetAddress serverAddress = InetAddress.getByName(ipNumber); Log.d(TAG, "Connecting..."); /** * Sending empty message with static int value from MainActivity * to update UI ( 'Connecting...' ). * * @see com.example.turnmeoff.MainActivity.CONNECTING */ mHandler.sendEmptyMessageDelayed(MainActivity.CONNECTING,1000); /** * Here the socket is created with hardcoded port. * Also the port is given in IpGetter class. * * @see com.example.turnmeoff.IpGetter */ Socket socket = new Socket(serverAddress, 4444); try { // Create PrintWriter object for sending messages to server. out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true); //Create BufferedReader object for receiving messages from server. in = new BufferedReader(new InputStreamReader(socket.getInputStream())); Log.d(TAG, "In/Out created"); ``` After that, the infinite loop is started and is working as far as the response come: ```java //Sending message with command specified by AsyncTask this.sendMessage(command); // mHandler.sendEmptyMessageDelayed(MainActivity.SENDING,2000); //Listen for the incoming messages while mRun = true while (mRun) { incomingMessage = in.readLine(); if (incomingMessage != null && listener != null) { /** * Incoming message is passed to MessageCallback object. * Next it is retrieved by AsyncTask and passed to onPublishProgress method. * */ listener.callbackMessageReceiver(incomingMessage); } incomingMessage = null; } Log.d(TAG, "Received Message: " +incomingMessage); } catch (Exception e) { Log.d(TAG, "Error", e); mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000); } finally { out.flush(); out.close(); in.close(); socket.close(); mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 3000); Log.d(TAG, "Socket Closed"); } } catch (Exception e) { Log.d(TAG, "Error", e); mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000); } }
After getting message, the callback is getting involved and is pushing the message to 'onProgressUpdate(..)' method. In this method we are stopping the client and getting to the end:
@Override protected void onProgressUpdate(String... values) { super.onProgressUpdate(values); Log.d(TAG, "In progress update, values: " + values.toString()); if(values[0].equals("shutdown")){ tcpClient.sendMessage(COMMAND); tcpClient.stopClient(); mHandler.sendEmptyMessageDelayed(MainActivity.SHUTDOWN, 2000); }else{ tcpClient.sendMessage("wrong"); mHandler.sendEmptyMessageDelayed(MainActivity.ERROR, 2000); tcpClient.stopClient(); } }
When client is stopped, we are going to 'onPostExecute(...)' method to check if client is stopped:
@Override protected void onPostExecute(TCPClient result){ super.onPostExecute(result); Log.d(TAG, "In on post execute"); if(result != null && result.isRunning()){ result.stopClient(); } mHandler.sendEmptyMessageDelayed(MainActivity.SENT, 4000); }
-
Between all of these steps you can see the Handler object. It is used for updating the UI, just look at the end off the MainActivity class.