需求
最近在公司中没有明确要做的东西,开会的时候说可能会做手机端的遥控器PS:我现在做的是电视盒子,然后就从网上看了一下,几个遥控器软件中就看悟空遥控器比较好用一点,也比较能让人接受,所以就准备写一个Demo 实现一下这个遥控器。
实现
当然就是这种操作肯定是需要一种连接,但是呢,假如说我们建立一个长连接(建立在网络端)的,这种肯定不符合我们的需求了,我们TV的操作需求其实只能是在眼睛可视范围之内进行操作,不满足这条件,我们的操作将会失去意义,所以我选择的是Socket 的连接,完成客户端按键命令的发送,
一、先看一下 Demo的界面
好吧 不知道为什么最近为什么图大小不能改变 现在就不管了
二、客户端
就是上面的图片中的功能,我们的设想是更具UI界面中的组件去操作TV的按键功能,看一下代码
class MainActivity : Activity(), View.OnClickListener {
private lateinit var mSocket: Socket
private lateinit var button_tv: Button
private var isConnection: Boolean = false
private var ip: String = "192.168.1.95"
private val PORT: Int = 54321
override fun onClick(p0: View?) {
when (p0?.id) {
R.id.button_left -> sendOrderCode(KeyEvent.KEYCODE_DPAD_LEFT)
R.id.button_up -> sendOrderCode(KeyEvent.KEYCODE_DPAD_UP)
R.id.button_right -> sendOrderCode(KeyEvent.KEYCODE_DPAD_RIGHT)
R.id.button_down -> sendOrderCode(KeyEvent.KEYCODE_DPAD_DOWN)
R.id.button_ok -> sendOrderCode(KeyEvent.KEYCODE_ENTER)
R.id.button_back -> sendOrderCode(KeyEvent.KEYCODE_BACK)
R.id.button_home -> sendOrderCode(KeyEvent.KEYCODE_HOME)
R.id.button_add -> sendOrderCode(KeyEvent.KEYCODE_VOLUME_UP)
R.id.button_minus -> sendOrderCode(KeyEvent.KEYCODE_VOLUME_DOWN)
}
}
private fun sendOrderCode(code: Int) {
Thread(Runnable {
var socket: Socket? = null
try {
socket = Socket("IP 地址", 6868)
} catch (e: IOException) {
e.printStackTrace()
}
var os: OutputStream? = null
try {
os = socket!!.getOutputStream()
} catch (e: IOException) {
e.printStackTrace()
}
val ps = PrintStream(os!!)
ps.println(code)
ps.flush()
ps.close()
try {
os.close()
} catch (e: IOException) {
e.printStackTrace()
}
try {
socket!!.close()
} catch (e: IOException) {
e.printStackTrace()
}
}).start()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
button_tv = findViewById(R.id.button_connection_tv)
initClick()
}
private fun initClick() {
findViewById<Button>(R.id.button_left).setOnClickListener(this)
findViewById<Button>(R.id.button_up).setOnClickListener(this)
findViewById<Button>(R.id.button_right).setOnClickListener(this)
findViewById<Button>(R.id.button_down).setOnClickListener(this)
findViewById<Button>(R.id.button_ok).setOnClickListener(this)
findViewById<Button>(R.id.button_back).setOnClickListener(this)
findViewById<Button>(R.id.button_home).setOnClickListener(this)
findViewById<Button>(R.id.button_add).setOnClickListener(this)
findViewById<Button>(R.id.button_minus).setOnClickListener(this)
button_tv.setOnClickListener({
object : Thread() {
override fun run() {
val connectionTV = connectionTV()
if (connectionTV) {
Log.e("状态", "连接状态" + "成功")
isConnection = true
}
}
}
})
}
private fun connectionTV(): Boolean {
mSocket = Socket(ip, PORT)
while (true) {
val inputStream = DataInputStream(mSocket.getInputStream())
val s = inputStream.readUTF()
if (!TextUtils.isEmpty(s)) {
if ("ok" == s) {
return true
}
}
return false
}
}
}
我们通过Socket实现连接 ,发送按键指令
上述代码中出现的KeyEvent 的按键的值均为Demo图对应的值,其他址可以看 KeyEvent 的键值
三 、 服务端
服务端,就是TV 上的Demo ,TV上的项目当然我们使用的就是服务去操作按键指令了,先看一下代码
public class TVService extends Service {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
int keyCode = msg.arg1;
onKeyEvent(keyCode);
}
}
};
public static void onKeyEvent(final int keyCode) {
new Thread() {
public void run() {
try {
Instrumentation inst = new Instrumentation();
inst.sendKeyDownUpSync(keyCode);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
public TVService() {
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startSocketServer();
return super.onStartCommand(intent, flags, startId);
}
private void startSocketServer() {
new Thread(new Runnable() {
@Override
public void run() {
ServerSocket server;
try {
server = new ServerSocket(6868);
System.out.println("Tvsr:Server Started!");
while (true) {
System.out.println("Tvsr:Server Runing!");
Socket client = server.accept();
InputStream ins = client.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(ins));
final String tmp = br.readLine();
final int keyCode = Integer.parseInt(tmp);
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();
message.what = 0;
message.arg1 = keyCode;
mHandler.sendMessage(message);
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
上述的代码就是服务端的代码,使用的是Instrumentation 做事件注入 ,运行的时候你会发现需要添加一个权限
<uses-permission android:name="android.permission.INJECT_EVENTS"/>
这是一个系统权限 ,你的操作要是在Activity中,是不需要这个权限的,但是服务中去操作其它应用程序时需要这个权限,添加这个权限之后会报错,当然,这时我们需要将我们的应用签名成系统应用签名的文件,并且在AndroidManifest.xml 的manifest节点上添加android:sharedUserId=”android.uid.system”该属性,
当然很操蛋的是如何将应用打包成系统签名文件的应用,我很幸运我们的盒子的签名文件是一样的,并且是提供给我的,这就加速了我的Demo的完成,要是你没有系统签名,那就去寻找,要是找不到,那你就蛋碎了(自己操的)哈哈。。。。