最近一年以来,博主一直在搞密室逃脱方面的东西。涉及到的技术很杂,不少也很冷门。
前段时间有这样一个有意思的需求:要求服务器在达到一定条件时唤醒一台待机的投影仪自动进行播放,并且在不同的条件下播放不同的影片。
博主考察了一下市场发现现在市面上有这样几种投影仪:
- 普通投影仪,需要接一台电脑,在电脑上选择播放的影片,电脑显示什么投影仪做为显示设备就播放什么。这个想做自动化需要和播放影片的电脑进行通讯,但一台电脑只作为一个视频播放器使用有点大材小用。
- 可以插U盘的投影仪,这种投影仪很好的取代了播放设备,直接读取U盘中的内容,在投影仪上播放。这类投影仪一般只能用遥控器进行控制,不能被服务器控制,所以也不可取。
- 最后博主在某宝上发现了一种小型带Android系统的投影仪,就是这个样子了,名字叫CooLux(中文名酷乐视),有兴趣的朋友可以搜索一下。
这个小型投影仪本身带Android系统,传统实体按键变成了面板上的上、下、左、右、OK、返回、菜单按键,屏幕变成了投影灯泡,把影像直接投到墙上。有了Android系统就意味着可以像控制手机一样去控制这台投影仪,什么选择影片、无线通讯、控制待机、唤醒什么的都不是问题。
现在需求来了,投影仪如何接到推送消息然后自动唤醒屏幕并在播放完毕后自动关闭屏幕?我的解决思路如下:
- 在服务器上安装androidpn服务程序,对在线手机进行推送(请大家自行参考androidpn教程)。
- 应用中集成androidpn-client接收推送消息(请大家自行参考androidpn教程)。
- 改造androidpn-client,在收到推送消息后,向主Activity发送intent,传送推送消息内容(其中包括要播放哪部影片的索引)。
在androidpn-client的NotificationReceiver类onReceive函数中,我们在收到消息后不吧消息交给Notifier处理,而是直接向主Activity发送intent。
// Notifier notifier = new Notifier(context); // notifier.notify(notificationId, notificationApiKey, // notificationTitle, notificationMessage, notificationUri,notificationFrom,packetId); //直接转到对应的Activity Intent nintent = new Intent(context,ProjectorPlayerActivity.class); nintent.putExtra(Constants.NOTIFICATION_ID,notificationId); nintent.putExtra(Constants.NOTIFICATION_API_KEY,notificationApiKey); nintent.putExtra(Constants.NOTIFICATION_TITLE,notificationTitle); nintent.putExtra(Constants.NOTIFICATION_MESSAGE,notificationMessage); nintent.putExtra(Constants.NOTIFICATION_URI,notificationUri); nintent.putExtra(Constants.NOTIFICATION_FROM, notificationFrom); nintent.putExtra(Constants.PACKET_ID, packetId); nintent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(nintent);
- Activity收到intent后,判断屏幕是否关闭,如果关闭执行唤醒屏幕操作,再对消息中指定的影片进行播放。
Activity的onNewIntent函数处理获得的intent
DeviceUtils类的内容如下,主要对硬件设备进行一些操作,目前包括振动、唤醒屏幕、关闭屏幕@Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); String urivalue = intent.getStringExtra(Constants.NOTIFICATION_URI); if(urivalue==null || urivalue.equals("")) //判断URI消息是否有效 return; DeviceUtils.vibrate(this, 500); //让手机振动500ms if(DeviceUtils.isScreenLocked(this)) //判断手机是否处于屏幕关闭状态 DeviceUtils.wakeScreen(this); //如果处于关闭屏幕状态则唤醒屏幕 <span style="white-space:pre"> </span>//此处做播放影片的处理 }
/** * 设备工具类,操作一些硬件设备功能 * Created by fengchong on 16/1/18. */ public class DeviceUtils { //锁屏部分------------------------- //继承了设备管理器的广播类,没做任何操作 public static class AdminReceiver extends DeviceAdminReceiver{} private static DevicePolicyManager policyManager; private static ComponentName componentName; private static final int MY_REQUEST_CODE = 9999; /** * 初始化锁屏 * @param context */ public static void initLockScreen(Context context){ policyManager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); componentName = new ComponentName(context,DeviceUtils.AdminReceiver.class); } /** * 激活设备管理器,设置app为激活状态 */ public static void activeDeviceManager(Activity activity){ Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN); intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,componentName); intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,"激活一键锁屏"); activity.startActivityForResult(intent, MY_REQUEST_CODE); } /** * 判断是否处于锁屏状态 * @param c * @return 返回ture为锁屏,返回flase为未锁屏 */ public final static boolean isScreenLocked(Context c) { KeyguardManager km = (KeyguardManager) c.getSystemService(c.KEYGUARD_SERVICE); return km.inKeyguardRestrictedInputMode(); } /** * 被Activity的onActivityResult函数调用 * @param activity * @param requestCode * @param resultCode * @param data */ public static void onActivityResult(Activity activity,int requestCode,int resultCode,Intent data){ if(requestCode==MY_REQUEST_CODE && resultCode== Activity.RESULT_OK){ policyManager.lockNow(); activity.finish(); }else{ activity.finish(); } } /** * 锁定屏幕操作 * @param activity */ public static void lockScreen(Activity activity){ if(policyManager.isAdminActive(componentName)){ policyManager.lockNow(); activity.finish(); }else{ activeDeviceManager(activity); } } //锁屏部分------------------------- //唤醒屏幕部分---------------------- private static KeyguardManager km; //键盘管理 private static PowerManager pm; //电源管理 private static PowerManager.WakeLock wakeLock; //屏幕唤醒对象 /** * 初始化唤醒屏幕 * @param activity */ public static void initWakeScrenUnlock(Activity activity){ activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK,"bright"); wakeLock.acquire(); } /** * 唤醒屏幕 */ public static void wakeScreen(Activity activity){ //屏幕解锁 km= (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE); KeyguardManager.KeyguardLock kl = km.newKeyguardLock("unLock"); kl.disableKeyguard(); //屏幕唤醒 if(wakeLock==null) { pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK,"bright"); } wakeLock.acquire(); wakeLock.release(); } //唤醒屏幕部分---------------------- //手机振动部分部分---------------------- /** * 手机震动函数 * @param activity * @param milliseconds */ public static void vibrate(final Activity activity, long milliseconds) { Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE); vib.vibrate(milliseconds); } //手机振动部分部分---------------------- }
关闭屏幕部分参考了这篇文章(表示感谢)http://www.cnblogs.com/tianzhijiexian/p/3920257.html,其中还会包含其他一些资源文件大家可自行参考。
唤醒屏幕部分除了以上的代码还需要在AndroidManifest.xml中加入权限
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
- 影片播放后自动执行关闭屏幕操作,节约电能。
在合适的位置直接执行DeviceUtils.lockScreen函数既可。
源码在这里:https://github.com/sorcerer310/BakerStreet42ProjectorPlayer