我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(三)Android客户端功能实现

我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(一)PC服务器端
(地址:http://blog.csdn.net/ouyang_peng/article/details/47004617)

我的Android进阶之旅------>Android实现用Android手机控制PC端的关机和重启的功能(二)Android客户端功能展示
(地址:http://blog.csdn.net/ouyang_peng/article/details/47005739)

通过以上两篇文章,我们了解了Android实现用Android手机控制PC端的关机和重启的的大概功能,现在我们来实现Android客户端的代码。

首先来看看整个项目的结构,如下图所示:

                                   

第一步:扫描局域网内所有PC,看是否有PC端的服务器在运行并监听30000端口。

此界面的布局文件为:/res/layout/layout_scanip.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <RelativeLayout
        android:id="@+id/ll"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:orientation="vertical" >
        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:orientation="vertical" >
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="@string/scaning" />
            <ProgressBar
                android:id="@+id/progressBar"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="100dp"
                android:layout_height="1dp" />
        </LinearLayout>
    </RelativeLayout>

</RelativeLayout>

扫描IP的Activity为com.oyp.shutdown.ScanActivity,代码如下:

package com.oyp.shutdown;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Window;
import android.widget.ProgressBar;
import android.widget.Toast;

public class ScanActivity extends Activity {
	private MyWifiManager myWifiManager = null;
	private String serverIP = "", resultIP = "";
	private ScanIPThread scanThread = null;
	private ProgressBar progressBar;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getWindow().requestFeature(Window.FEATURE_NO_TITLE);
		int height = getWindow().getWindowManager().getDefaultDisplay()
				.getHeight();
		int width = getWindow().getWindowManager().getDefaultDisplay()
				.getWidth();
		setContentView(R.layout.layout_scanip);
		progressBar = (ProgressBar) findViewById(R.id.progressBar);
		myWifiManager = new MyWifiManager(ScanActivity.this);
		scanThread = new ScanIPThread();
		scanThread.start();
	}

	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 1000:
				Toast.makeText(ScanActivity.this, getString(R.string.find),
						Toast.LENGTH_SHORT).show(); // 找到可连接PC
				Intent controlIntent = new Intent(ScanActivity.this,
						ControlActivity.class);
				// 将可以连接的ip发过去
				controlIntent.putExtra("ip", (String) msg.obj);
				startActivity(controlIntent);
				finish();
				break;
			case 2000:
				Toast.makeText(ScanActivity.this, getString(R.string.notFind),
						Toast.LENGTH_SHORT).show(); // 没有找到可连接PC
				Intent reScanIntent = new Intent(ScanActivity.this,
						ReScanActivity.class);
				startActivity(reScanIntent);
				finish();
				break;
			default:
				progressBar.setMax(254);
				progressBar.setProgress(msg.what);
				break;
			}
			super.handleMessage(msg);
		}
	};

	// 扫描连接的WiFi所在网段开启了30000端口的C类ip
	// 例如,wifi的ip是192.168.1.1 则扫描 192.168.1.1-192.168.1.254
	class ScanIPThread extends Thread {
		@Override
		public void run() {
			serverIP = myWifiManager.getServerIp();
			int t = serverIP.lastIndexOf(".") + 1;
			resultIP = serverIP.substring(0, t);
			boolean flag = false;
			for (int i = 1; i < 255; i++) {
				try {
					Socket socket = new Socket();
					InetSocketAddress s = new InetSocketAddress(resultIP + i,
							30000);
					socket.connect(s, 50);
					Message message = new Message();
					message.what = 1000;
					message.obj = resultIP + i;
					handler.sendMessage(message);
					flag = true;
					socket.close();
					break;
				} catch (IOException e) {
					handler.sendEmptyMessage(i);
				}
			}
			if (!flag) {
				handler.sendEmptyMessage(2000);
			}
			super.run();
		}
	}
}

第二步:如果没有扫描到有PC端的服务器在运行并监听30000端口,则重新扫描或者退出。如上述代码中,当msg.what=2000时,重新进行扫描。

此界面的布局文件为:/res/layout/layout_rescan.xml

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

    <Button
        android:id="@id/btnReScan"
        style="@style/StyleButton"
        android:text="@string/rescan" />

    <Button
        android:id="@id/btnClose"
        style="@style/StyleButtonClose"
        android:text="@string/close" />

</LinearLayout>

重新扫描IP的Activity为com.oyp.shutdown.ReScanActivity,代码如下:

package com.oyp.shutdown;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;

public class ReScanActivity extends Activity implements OnClickListener {
	private Button btn_rescan, btn_close;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
		setContentView(R.layout.layout_rescan);
		getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
				R.drawable.acticon);

		btn_rescan = (Button) findViewById(R.id.btnReScan);
		btn_close = (Button) findViewById(R.id.btnClose);

		btn_rescan.setOnClickListener(this);
		btn_close.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btnReScan:
			Intent intent = new Intent(ReScanActivity.this,ScanActivity.class);
			startActivity(intent);
			finish();
			break;
		case R.id.btnClose:
			finish();
			break;
		default:
			break;
		}
	}
}

第二步:扫描到了有PC端的服务器在运行并监听30000端口,则控制PC端关机、重启或者取消关机。如上述代码中,当msg.what=1000时,则打开控制PC端界面。

此界面的布局文件为:/res/layout/layout_control.xml

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

    <Button
        android:id="@id/btnShutdown"
        style="@style/StyleButton"
        android:text="@string/shutdown" />

    <Button
        android:id="@id/btnReboot"
        style="@style/StyleButton"
        android:text="@string/reboot" />

    <Button
        android:id="@id/btnCancel"
        style="@style/StyleButton"
        android:text="@string/cancel" />

    <Button
        android:id="@id/btnClose"
        style="@style/StyleButtonClose"
        android:text="@string/close" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="6.0dip"
        android:text=" "
        android:textSize="12.0dip" />

</LinearLayout>

控制PC端的Activity为com.oyp.shutdown.ControlActivity,代码如下:

package com.oyp.shutdown;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.Button;
import android.widget.Toast;

public class ControlActivity extends Activity implements OnClickListener {
	private Button btn_shutdown, btn_restart, btn_cancel, btn_close;
	private Socket clientSocket;// 客户端socket
	private DataOutputStream dataOutput = null;// 客户端发送数据
	private DataInputStream dataInput = null;// 客户端接收数据
	private String connIP = "";
	private ConnThread connThread = null;
	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case 1:
				Toast.makeText(ControlActivity.this, (String) msg.obj,
						Toast.LENGTH_SHORT).show();
				break;
			default:
				break;
			}
			super.handleMessage(msg);
		}
	};

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		getWindow().requestFeature(Window.FEATURE_LEFT_ICON);
		setContentView(R.layout.layout_control);
		getWindow().setFeatureDrawableResource(Window.FEATURE_LEFT_ICON,
				R.drawable.acticon);

		btn_shutdown = (Button) findViewById(R.id.btnShutdown);
		btn_restart = (Button) findViewById(R.id.btnReboot);
		btn_cancel = (Button) findViewById(R.id.btnCancel);
		btn_close = (Button) findViewById(R.id.btnClose);

		Intent intent = getIntent();
		connIP = intent.getStringExtra("ip");

		btn_shutdown.setOnClickListener(this);
		btn_restart.setOnClickListener(this);
		btn_cancel.setOnClickListener(this);
		btn_close.setOnClickListener(this);
	}

	@Override
	public void onClick(View v) {
		// 连接服务器
		switch (v.getId()) {
		case R.id.btnShutdown:
			final String shutdown = "shutdown";
			if (connThread != null) {
				connThread.interrupt();
			}
			connThread = new ConnThread(connIP, 30000, shutdown);
			connThread.start();
			break;
		case R.id.btnReboot:
			final String reboot = "reboot";
			if (connThread != null) {
				connThread.interrupt();
			}
			connThread = new ConnThread(connIP, 30000, reboot);
			connThread.start();
			break;
		case R.id.btnCancel:
			final String cancel = "cancel";
			if (connThread != null) {
				connThread.interrupt();
			}
			connThread = new ConnThread(connIP, 30000, cancel);
			connThread.start();
			break;
		case R.id.btnClose:
			finish();
			break;
		default:
			break;
		}
	}

	class ConnThread extends Thread {
		private String ip;
		private int port;
		private String content;

		public ConnThread(String ip, int port, String content) {
			this.ip = ip;
			this.port = port;
			this.content = content;
		}

		@Override
		public void run() {
			try {
				clientSocket = new Socket(ip, port);
				while (true) {
					dataOutput = new DataOutputStream(
							clientSocket.getOutputStream());
					dataInput = new DataInputStream(
							clientSocket.getInputStream());
					String msg = "";
					if ((dataOutput != null) && (!content.equals(""))) {
						dataOutput.writeUTF(content);
					}
					msg = dataInput.readUTF();
					if (msg != null && !"".equals(msg)) {
						Message message = new Message();
						message.what = 1;
						message.obj = msg;
						handler.sendMessage(message);
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					if (dataOutput != null) {
						dataOutput.close();
					}
					if (dataInput != null) {
						dataInput.close();
					}
					if (clientSocket != null) {
						clientSocket = null;
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			super.run();
		}
	}
}

其中MyWifiManager.java代码如下:

package com.oyp.shutdown;

import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;

public class MyWifiManager {

	private WifiManager wifiManager;
	private WifiInfo wifiInfo;
	private DhcpInfo dhcpInfo;
	public MyWifiManager(Context context){
		wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
		wifiInfo = wifiManager.getConnectionInfo();
		dhcpInfo = wifiManager.getDhcpInfo();
	}
	//得到本机ip
	public String getLocalIp(){
		return FormatString(dhcpInfo.ipAddress);
	}
	//得到服务器ip(热点ip)
	public String getServerIp(){
		return FormatString(dhcpInfo.serverAddress);
	}
	//转换ip格式为*.*.*.*
	public String FormatString(int value){
		String strValue="";
		byte[] ary = intToByteArray(value);
		for(int i=ary.length-1;i>=0;i--){
			strValue+=(ary[i]&0xFF);
			if(i>0){
				strValue+=".";
			}
		}
		return strValue;
	}
	public byte[] intToByteArray(int value){
		byte[] b=new byte[4];
		for(int i=0;i<4;i++){
			int offset = (b.length-1-i)*8;
			b[i]=(byte) ((value>>>offset)&0xFF);
		}
		return b;
	}
}

定义控件ID的文件为/res/values/id.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item type="id" name="btnAirplane">false</item>
    <item type="id" name="btnShutdown">false</item>
    <item type="id" name="btnReScan">false</item>
    <item type="id" name="btnCancel">false</item>
    <item type="id" name="btnReboot">false</item>
    <item type="id" name="btnRecovery">false</item>
    <item type="id" name="btnBootloader">false</item>
    <item type="id" name="btnClose">false</item>
</resources>

/res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">控制PC</string>
    <string name="shutdown">关机</string>
    <string name="rescan">重新扫描</string>
    <string name="reboot">重启</string>
    <string name="cancel">取消</string>
    <string name="please_wait">请稍等......</string>
    <string name="warning">错误</string>
    <string name="root">无法获得 Root 权限!</string>
    <string name="close">退出</string>
    <string name="ok">确定</string>
    <string name="scaning">正在扫描......</string>
    <string name="find">找到可连接PC.</string>
    <string name="notFind">没有找到可连接PC</string>
</resources>

/res/values/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="StyleButton">
        <item name="android:textSize">16.0dip</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textColor">#ffffffff</item>
        <item name="android:layout_gravity">center_horizontal</item>
        <item name="android:background">@drawable/btn_selector</item>
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginLeft">30.0dip</item>
        <item name="android:layout_marginRight">30.0dip</item>
        <item name="android:layout_marginBottom">8.0dip</item>
    </style>
    <style name="StyleButtonClose">
        <item name="android:textSize">16.0dip</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textColor">#ffffffff</item>
        <item name="android:layout_gravity">center_horizontal</item>
        <item name="android:background">@drawable/btn_close_selector</item>
        <item name="android:layout_width">wrap_content</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="android:layout_marginLeft">30.0dip</item>
        <item name="android:layout_marginRight">30.0dip</item>
        <item name="android:layout_marginBottom">8.0dip</item>
    </style>
</resources>

项目描述文件:AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.oyp.shutdown"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/icon"
        android:label="@string/app_name" >
        <activity
            android:name=".ScanActivity"
            android:theme="@android:style/Theme.Dialog" >
             <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".ReScanActivity"
            android:icon="@drawable/icon"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.Dialog" >
        </activity>
        <activity
            android:name=".ControlActivity"
            android:icon="@drawable/icon"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.Dialog" >
        </activity>
    </application>
</manifest>

代码可以在此处下载:Android实现用Android手机控制PC端的关机和重启的功能  地址:(http://download.csdn.net/detail/qq446282412/8923991)

 

                            ====================================================================================
  作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址:http://blog.csdn.net/ouyang_peng

====================================================================================

 

时间: 2024-08-02 22:15:31

我的Android进阶之旅------&gt;Android实现用Android手机控制PC端的关机和重启的功能(三)Android客户端功能实现的相关文章

我的Android进阶之旅------&amp;gt;Android颜色值(RGB)所支持的四种常见形式

Android中颜色值是通过红(Red).绿(Green).蓝(Blue)三原色,以及一个透明度(Alpha)值来表示的,颜色值总是以井号(#)开头,接下来就是Alpha-Red-Green-Blue的形式.其中Alpha值可以省略,如果省略了Alpha的值,那么该颜色默认是完全不透明的. Android的颜色值支持常见的四种形式如下所示: #RGB:分别指定红.绿.蓝三原色的值(只支持0~f这16级颜色)来代表颜色. #ARGB:分别指定红.绿.蓝三原色的值(只支持0~f这16级颜色)及透明度

我的Android进阶之旅------&amp;gt;解决Jackson等第三方转换Json的开发包在开启混淆后转换的实体类数据都是null的bug

1.错误描述 今天测试人员提了一个bug,说使用我们的app出现了闪退的bug,后来通过debug断点调试,发现我们的app转换服务器发送过来的json数据后,都是为null.而之前已经提测快一个月的功能,一直都是稳定的,为什么现在会报java.lang.NullPointerException. 2.错误原因 原来我提测了一个月的APP版本一直没有打开混淆开关,而出问题的这个APP版本在即将要发布出去的时候打开了混淆开关.这样的话,我那些要通过转换json数据为bean实体类,因为没有在pro

我的Android进阶之旅------&amp;gt;Android疯狂连连看游戏的实现之状态数据模型(三)

对于游戏玩家而言,游戏界面上看到的"元素"千变万化:但是对于游戏开发者而言,游戏界面上的元素在底层都是一些数据,不同数据所绘制的图片有所差异而已.因此建立游戏的状态数据模型是实现游戏逻辑的重要步骤. 1.定义数据模型 连连看的界面是一个NxM的"网格",每个网格上显示一张图片.而这个网格只需要一个二维数组来定义即可,而每个网格上所显示的图片,对于底层数据模型来说,不同的图片对于着不同的数值即可. 对于上图所示的数据模型,只要让数值为0的网格上不绘制图片,其他数值的网

我的Android进阶之旅------&amp;gt;介绍一款集录制与剪辑为一体的屏幕GIF 动画制作工具 GifCam

由于上一篇文章:我的Android进阶之旅------>Android之动画之Frame Animation实例 中展示的是Frame动画效果,但是之前我是将图片截取下来,不好说明确切的动画过程,因此今天百度了一下gif动画的制作工具,找到了这款不错的软件GifCam.然后我使用该软件将动画制作成gif动画,然后更新了上一篇文章上传了新制作的gif动画,使博客的说明效果更佳. 该软件的界面如下所示: 该软件的操作步骤如下: 1.拖动 GifCam 标题栏,改变其位置,并调整 GifCam 窗口大

我的Android进阶之旅------&amp;gt;Android疯狂连连看游戏的实现之开发游戏界面(二)

连连看的游戏界面十分简单,大致可以分为两个区域: 游戏主界面区 控制按钮和数据显示区 1.开发界面布局 本程序使用一个RelativeLayout作为整体的界面布局元素,界面布局上面是一个自定义组件,下面是一个水平排列的LinearLayout. 下面是本程序的布局文件:/res/layout/main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android=

我的Android进阶之旅------&amp;gt;Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)

正如在<我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)>一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piece对象时,程序会直接调用ImageUtil的getPlayImages()方法去获取图片,该方法会随机从res/drawable目录中取得N张图片. 下面是res/drawable目录视图: 为了让getPlayImages()方法能随机从res/drawable目录中取得N张图片,具体实现分为以下几步: 通

我的Android进阶之旅------&amp;gt; Android为TextView组件中显示的文本添加背景色

通过上一篇文章 我的Android进阶之旅------> Android在TextView中显示图片方法 (地址:http://blog.csdn.net/ouyang_peng/article/details/46916963)      我们学会了在TextView中显示图片的方法,现在我们来学习如何为TextView组件中显示的文本添加背景色.要求完成的样子如图所示: 首先来学习使用BackgroundColorSpan对象设置文字背景色,代码如下: TextView textView=(

我的Android进阶之旅------&amp;gt;HTTP 返回状态值详解

(本文转载于:http://blog.csdn.net/ithomer/article/details/10240351) 当用户点击或搜索引擎向网站服务器发出浏览请求时,服务器将返回Http Header Http头信息状态码,常见几种如下: 1.Http/1.1 200 OK 访问正常   表示成功访问,为网站可正常访问时的状态. 2.Http/1.1 301 Moved Permanently 301重定向永久重定向   对搜索引擎相对友好的跳转方式,当网站更换域名时可将原域名作301永久

我的Android进阶之旅------&amp;gt;Android权限参考大全

访问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES ,读取或写入登记check-in数据库属性表的权限 获取错略位置 android.permission.ACCESS_COARSE_LOCATION,通过WiFi或移动基站的方式获取用户错略的经纬度信息,定位精度大概误差在30~1500米 获取精确位置 android.permission.ACCESS_FINE_LOCATION,通过GPS芯片接收卫星的定位信息,定位精度达10米以内 访问定