├── .classpath ├── .gitignore ├── .project ├── .settings └── org.eclipse.jdt.core.prefs ├── Android.mk ├── AndroidManifest.xml ├── README.md ├── apk ├── Bluetooth.apk ├── TestKey.apk ├── device-2015-01-21-152934.png ├── device-2015-02-07-105356.png ├── device-2015-02-07-105501.png ├── device-2015-02-07-121646.png └── last_number_redail.png ├── gen └── com │ └── example │ └── testkey │ ├── BuildConfig.java │ └── R.java ├── ic_launcher-web.png ├── libs └── android-support-v4.jar ├── proguard-project.txt ├── project.properties ├── res ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── drawable-xhdpi │ └── ic_launcher.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── layout │ └── activity_main.xml ├── menu │ └── main.xml ├── raw │ ├── lapple.mp3 │ └── silence10sec.mp3 ├── values-sw600dp │ └── dimens.xml ├── values-sw720dp-land │ └── dimens.xml ├── values-v11 │ └── styles.xml ├── values-v14 │ └── styles.xml └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── src └── com └── example └── testkey ├── KeyService.java ├── MainActivity.java └── MusicIntentReceiver.java /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | !TestKey.apk 4 | !Bluetooth.apk 5 | *.ap_ 6 | 7 | # Files for the Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | 17 | # Gradle files 18 | .gradle/ 19 | build/ 20 | 21 | # Local configuration file (sdk path, etc) 22 | local.properties 23 | 24 | # Proguard folder generated by Eclipse 25 | proguard/ 26 | 27 | # Log Files 28 | *.log 29 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | TestKey 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 3 | org.eclipse.jdt.core.compiler.compliance=1.6 4 | org.eclipse.jdt.core.compiler.source=1.6 5 | -------------------------------------------------------------------------------- /Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | 3 | include $(CLEAR_VARS) 4 | 5 | LOCAL_MODULE_TAGS := optional 6 | 7 | LOCAL_SRC_FILES := $(call all-java-files-under, src) 8 | 9 | LOCAL_PACKAGE_NAME := TestKey 10 | 11 | LOCAL_CERTIFICATE := platform 12 | 13 | include $(BUILD_PACKAGE) 14 | 15 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Android按键测试程序 2 | --- 3 | ### 物理按键测试 4 | 正常安装启动后就可以检测 5 | 6 | ### 蓝牙耳机播放音乐相关按键测试 7 | 需要额外播放音乐才能监听 8 | 9 | ### 蓝牙耳机语音通话相关按键测试 10 | 其中: 11 | 12 | VOICE_COMMAND : 语音拨号键 13 | LAST_NUMBER_REDIAL : 末位重拨 14 | ... : ... 15 | 16 | 说明: 17 | 18 | VOICE_COMMAND 正常安装后就可以监听到 19 | LAST_NUMBER_REDIAL 需要将该APP放置到system/app目录下放可正常监听到 20 | 比如,当LAST_NUMBER_REDIAL被触发后会出现PHONE应用和TESTKEY争抢启动。 21 | ![github](https://github.com/kangear/TestKey/blob/master/apk/device-2015-01-21-152934.png "github") 22 | 23 | 监听到 末位重拨 按键: 24 | ![github](https://github.com/kangear/TestKey/blob/master/apk/last_number_redail.png "github") 25 | 26 | 27 | ### 20150122更 28 | 介于国产手机ROM进行了CALL_PRIVILEGED进行了恶意屏蔽导致无法正常使用,所以只有通过更改Bluetooth.apk对「末号重拨」进行重定向。 29 | 30 | apk/目录下TestKey.apk/Bluetooth.apk是最新的。 31 | 32 | 替换系统自带Bluetooth.apk后,这次正常方式安装TestKey.apk就可以了。 33 | 34 | 正常现象是:当按下「末号重拨」键时,不再进行「末号重拨」,而是调起TestKey.apk. 35 | 36 | 如果实验正常,那么下一步就是转向到「语音拨号」。 37 | 38 | ### 20150123更 重定向 39 | 1.不再监听VOICE_COMMAND且将LAST_NUMBER_REDIAL重定向到VOICE_COMMAND 40 | 41 | 只更新了TestKey.apk只重新安装它就好。 42 | 43 | 另:如果你的手机支持VOICE_COMMAND(语音拨号)时直接从睡眠(黑屏)中唤醒,那么就能唤醒。 44 | 45 | TestKey.apk目前只启动重定向的功能。 46 | 47 | 2.重定向后自行关闭 48 | 49 | ### 20150124更 选择性重定向 50 | 添加判断条件,当且仅当在BT连接/黑屏(锁屏)状态下进行重定向。 51 | 52 | 下午:更新源码,解决逻辑错误,已经测试。 53 | 54 | ### 20150205更 添加连接蓝牙后自动将黑屏时间设置为10秒 55 | 56 | ### 20150207更 添加连接蓝牙休眠时间设置为10秒断开设置为120秒 57 | 并添加了通知,在Miui中验证过了。 58 | 59 | ![github](https://github.com/kangear/TestKey/blob/master/apk/device-2015-02-07-105356.png "github") 60 | 61 | ![github](https://github.com/kangear/TestKey/blob/master/apk/device-2015-02-07-105501.png "github") 62 | 63 | ![github](https://github.com/kangear/TestKey/blob/master/apk/device-2015-02-07-121646.png "github") 64 | -------------------------------------------------------------------------------- /apk/Bluetooth.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/apk/Bluetooth.apk -------------------------------------------------------------------------------- /apk/TestKey.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/apk/TestKey.apk -------------------------------------------------------------------------------- /apk/device-2015-01-21-152934.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/apk/device-2015-01-21-152934.png -------------------------------------------------------------------------------- /apk/device-2015-02-07-105356.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/apk/device-2015-02-07-105356.png -------------------------------------------------------------------------------- /apk/device-2015-02-07-105501.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/apk/device-2015-02-07-105501.png -------------------------------------------------------------------------------- /apk/device-2015-02-07-121646.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/apk/device-2015-02-07-121646.png -------------------------------------------------------------------------------- /apk/last_number_redail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/apk/last_number_redail.png -------------------------------------------------------------------------------- /gen/com/example/testkey/BuildConfig.java: -------------------------------------------------------------------------------- 1 | /** Automatically generated file. DO NOT MODIFY */ 2 | package com.example.testkey; 3 | 4 | public final class BuildConfig { 5 | public final static boolean DEBUG = true; 6 | } -------------------------------------------------------------------------------- /gen/com/example/testkey/R.java: -------------------------------------------------------------------------------- 1 | /* AUTO-GENERATED FILE. DO NOT MODIFY. 2 | * 3 | * This class was automatically generated by the 4 | * aapt tool from the resource data it found. It 5 | * should not be modified by hand. 6 | */ 7 | 8 | package com.example.testkey; 9 | 10 | public final class R { 11 | public static final class attr { 12 | } 13 | public static final class dimen { 14 | /** Default screen margins, per the Android Design guidelines. 15 | 16 | Customize dimensions originally defined in res/values/dimens.xml (such as 17 | screen margins) for sw720dp devices (e.g. 10" tablets) in landscape here. 18 | 19 | */ 20 | public static final int activity_horizontal_margin=0x7f050000; 21 | public static final int activity_vertical_margin=0x7f050001; 22 | } 23 | public static final class drawable { 24 | public static final int ic_launcher=0x7f020000; 25 | } 26 | public static final class id { 27 | public static final int action_settings=0x7f090002; 28 | public static final int is_test_bt_checkbox=0x7f090000; 29 | public static final int textView=0x7f090001; 30 | } 31 | public static final class layout { 32 | public static final int activity_main=0x7f030000; 33 | } 34 | public static final class menu { 35 | public static final int main=0x7f080000; 36 | } 37 | public static final class raw { 38 | public static final int lapple=0x7f040000; 39 | public static final int silence10sec=0x7f040001; 40 | } 41 | public static final class string { 42 | public static final int action_settings=0x7f060001; 43 | public static final int app_name=0x7f060000; 44 | public static final int hello_world=0x7f060002; 45 | } 46 | public static final class style { 47 | /** 48 | Base application theme, dependent on API level. This theme is replaced 49 | by AppBaseTheme from res/values-vXX/styles.xml on newer devices. 50 | 51 | 52 | Theme customizations available in newer API levels can go in 53 | res/values-vXX/styles.xml, while customizations related to 54 | backward-compatibility can go here. 55 | 56 | 57 | Base application theme for API 11+. This theme completely replaces 58 | AppBaseTheme from res/values/styles.xml on API 11+ devices. 59 | 60 | API 11 theme customizations can go here. 61 | 62 | Base application theme for API 14+. This theme completely replaces 63 | AppBaseTheme from BOTH res/values/styles.xml and 64 | res/values-v11/styles.xml on API 14+ devices. 65 | 66 | API 14 theme customizations can go here. 67 | */ 68 | public static final int AppBaseTheme=0x7f070000; 69 | /** Application theme. 70 | All customizations that are NOT specific to a particular API-level can go here. 71 | */ 72 | public static final int AppTheme=0x7f070001; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/ic_launcher-web.png -------------------------------------------------------------------------------- /libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/libs/android-support-v4.jar -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-15 15 | -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 25 | 26 | 31 | 32 | 37 | 38 | 43 | 44 | 48 | 49 | 54 | 55 | 62 | 63 | 64 | 68 | 69 | 74 | 75 | 82 | 83 | 84 | 88 | 89 | 94 | 95 | 102 | 103 | 104 | 110 | 111 | -------------------------------------------------------------------------------- /res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/raw/lapple.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/res/raw/lapple.mp3 -------------------------------------------------------------------------------- /res/raw/silence10sec.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kangear/TestKey/1d03b397ff774fe5f223a5554c8f98c55c86a213/res/raw/silence10sec.mp3 -------------------------------------------------------------------------------- /res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | 10 | -------------------------------------------------------------------------------- /res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | 8 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TestKey 5 | Settings 6 | Hello world! 7 | 动态休眠设置休眠时间,根据蓝牙连接状态设置合适的休眠时间 8 | 使能动态休眠-单位(秒) 9 | 连接时: 10 | 断开时: 11 | 禁用时: 12 | 13 | -------------------------------------------------------------------------------- /res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/com/example/testkey/KeyService.java: -------------------------------------------------------------------------------- 1 | package com.example.testkey; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.bluetooth.BluetoothAdapter; 5 | import android.bluetooth.BluetoothProfile; 6 | import android.content.Context; 7 | import android.os.PowerManager; 8 | import android.provider.Settings; 9 | import android.util.Log; 10 | import android.view.KeyEvent; 11 | import android.widget.Toast; 12 | 13 | public class KeyService { 14 | private static final String LOG_TAG = "KeyService"; 15 | private static int IS_NEED_REDIRECT = 0; 16 | private static final int BIT_BT_CONNECTED = (1 << 0); 17 | private static final int BIT_SCREEN_OFF = (1 << 1); 18 | private static final int NEED_REDIRECT = BIT_BT_CONNECTED | BIT_SCREEN_OFF; 19 | private final Context mContext; 20 | 21 | public KeyService(Context context) { 22 | this.mContext = context; 23 | } 24 | 25 | @SuppressLint("NewApi") 26 | public static boolean isNeedRedirect(Context context) { 27 | if(context == null) { 28 | return false; 29 | } 30 | IS_NEED_REDIRECT = 0; 31 | PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 32 | if(!powerManager.isScreenOn()) 33 | IS_NEED_REDIRECT |= BIT_SCREEN_OFF; 34 | else 35 | IS_NEED_REDIRECT &= ~BIT_SCREEN_OFF; 36 | 37 | BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 38 | IS_NEED_REDIRECT |= parseBtState(mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET)); 39 | 40 | Log.d(LOG_TAG, "IS_NEED_REDIRECT:" + IS_NEED_REDIRECT); 41 | return IS_NEED_REDIRECT == NEED_REDIRECT; 42 | } 43 | 44 | @SuppressLint("NewApi") 45 | public boolean isBtConect() { 46 | IS_NEED_REDIRECT = 0; 47 | BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 48 | IS_NEED_REDIRECT |= parseBtState(mBluetoothAdapter.getProfileConnectionState(BluetoothProfile.HEADSET)); 49 | 50 | Log.d(LOG_TAG, "IS_NEED_REDIRECT:" + IS_NEED_REDIRECT); 51 | return (IS_NEED_REDIRECT & BIT_BT_CONNECTED) == BIT_BT_CONNECTED; 52 | } 53 | 54 | public static int parseBtState(int state) { 55 | Log.i(LOG_TAG, "state:" + state); 56 | int result = 0; 57 | switch(state) { 58 | case BluetoothAdapter.STATE_CONNECTED: 59 | Log.i(LOG_TAG, "STATE_CONNECTED"); 60 | result |= BIT_BT_CONNECTED; 61 | break; 62 | case BluetoothAdapter.STATE_DISCONNECTING: 63 | case BluetoothAdapter.STATE_DISCONNECTED: 64 | Log.i(LOG_TAG, "STATE_DISCONNECTING"); 65 | result &= ~BIT_BT_CONNECTED; 66 | break; 67 | } 68 | return result; 69 | } 70 | 71 | public static String parseKeyCode(int keyCode) { 72 | String ret = ""; 73 | switch (keyCode) { 74 | case KeyEvent.KEYCODE_POWER: 75 | // 监控/拦截/屏蔽电源键 这里拦截不了 76 | ret = "get Key KEYCODE_POWER(KeyCode:" + keyCode + ")"; 77 | break; 78 | case KeyEvent.KEYCODE_RIGHT_BRACKET: 79 | // 监控/拦截/屏蔽返回键 80 | ret = "get Key KEYCODE_RIGHT_BRACKET"; 81 | break; 82 | case KeyEvent.KEYCODE_MENU: 83 | // 监控/拦截菜单键 84 | ret = "get Key KEYCODE_MENU"; 85 | break; 86 | case KeyEvent.KEYCODE_HOME: 87 | // 由于Home键为系统键,此处不能捕获 88 | ret = "get Key KEYCODE_HOME"; 89 | break; 90 | case KeyEvent.KEYCODE_DPAD_UP: 91 | // 监控/拦截/屏蔽上方向键 92 | ret = "get Key KEYCODE_DPAD_UP"; 93 | break; 94 | case KeyEvent.KEYCODE_DPAD_LEFT: 95 | // 监控/拦截/屏蔽左方向键 96 | ret = "get Key KEYCODE_DPAD_LEFT"; 97 | break; 98 | case KeyEvent.KEYCODE_DPAD_RIGHT: 99 | // 监控/拦截/屏蔽右方向键 100 | ret = "get Key KEYCODE_DPAD_RIGHT"; 101 | break; 102 | case KeyEvent.KEYCODE_DPAD_DOWN: 103 | // 监控/拦截/屏蔽下方向键 104 | ret = "get Key KEYCODE_DPAD_DOWN"; 105 | break; 106 | case KeyEvent.KEYCODE_DPAD_CENTER: 107 | // 监控/拦截/屏蔽中方向键 108 | ret = "get Key KEYCODE_DPAD_CENTER"; 109 | break; 110 | case KeyEvent.FLAG_KEEP_TOUCH_MODE: 111 | // 监控/拦截/屏蔽长按 112 | ret = "get Key FLAG_KEEP_TOUCH_MODE"; 113 | break; 114 | case KeyEvent.KEYCODE_VOLUME_DOWN: 115 | // 监控/拦截/屏蔽下方向键 116 | ret = "get Key KEYCODE_VOLUME_DOWN(KeyCode:" + keyCode + ")"; 117 | break; 118 | case KeyEvent.KEYCODE_VOLUME_UP: 119 | // 监控/拦截/屏蔽中方向键 120 | ret = "get Key KEYCODE_VOLUME_UP(KeyCode:" + keyCode + ")"; 121 | break; 122 | case 220: 123 | // case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: 124 | // 监控/拦截/屏蔽亮度减键 125 | ret = "get Key KEYCODE_BRIGHTNESS_DOWN(KeyCode:" + keyCode + ")"; 126 | break; 127 | case 221: 128 | // case KeyEvent.KEYCODE_BRIGHTNESS_UP: 129 | // 监控/拦截/屏蔽亮度加键 130 | ret = "get Key KEYCODE_BRIGHTNESS_UP(KeyCode:" + keyCode + ")"; 131 | break; 132 | case KeyEvent.KEYCODE_MEDIA_PLAY: 133 | ret = "get Key KEYCODE_MEDIA_PLAY(KeyCode:" + keyCode + ")"; 134 | break; 135 | case KeyEvent.KEYCODE_MEDIA_PAUSE: 136 | ret = "get Key KEYCODE_MEDIA_PAUSE(KeyCode:" + keyCode + ")"; 137 | break; 138 | case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 139 | ret = "get Key KEYCODE_MEDIA_PREVIOUS(KeyCode:" + keyCode + ")"; 140 | break; 141 | case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 142 | ret = "get Key KEYCODE_MEDIA_PLAY_PAUSE(KeyCode:" + keyCode + ")"; 143 | break; 144 | case KeyEvent.KEYCODE_MEDIA_NEXT: 145 | ret = "get Key KEYCODE_MEDIA_NEXT(KeyCode:" + keyCode + ")"; 146 | break; 147 | default: 148 | ret = "keyCode: " 149 | + keyCode 150 | + " (http://developer.android.com/reference/android/view/KeyEvent.html)"; 151 | break; 152 | } 153 | return ret; 154 | } 155 | 156 | /** 157 | * 获得休眠时间 毫秒 158 | */ 159 | public int getScreenOffTime() { 160 | int screenOffTime = 0; 161 | try { 162 | screenOffTime = Settings.System.getInt(mContext.getContentResolver(), 163 | Settings.System.SCREEN_OFF_TIMEOUT); 164 | } catch (Exception localException) { 165 | 166 | } 167 | return screenOffTime; 168 | } 169 | 170 | /** 171 | * 设置休眠时间 毫秒 172 | */ 173 | public void setScreenOffTime(int paramInt) { 174 | if(mContext != null) 175 | Toast.makeText(mContext, "TestKey:将休眠时间设置为" + paramInt/1000 + "秒.", Toast.LENGTH_SHORT).show(); 176 | try { 177 | Settings.System.putInt(mContext.getContentResolver(), 178 | Settings.System.SCREEN_OFF_TIMEOUT, paramInt); 179 | } catch (Exception localException) { 180 | localException.printStackTrace(); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/com/example/testkey/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.testkey; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | 8 | import android.annotation.SuppressLint; 9 | import android.app.Activity; 10 | import android.bluetooth.BluetoothAdapter; 11 | import android.content.BroadcastReceiver; 12 | import android.content.ComponentName; 13 | import android.content.Context; 14 | import android.content.Intent; 15 | import android.content.IntentFilter; 16 | import android.media.AudioManager; 17 | import android.os.Bundle; 18 | import android.util.Log; 19 | import android.view.KeyEvent; 20 | import android.view.View; 21 | import android.view.Window; 22 | import android.view.WindowManager; 23 | import android.widget.Button; 24 | import android.widget.CheckBox; 25 | import android.widget.CompoundButton; 26 | import android.widget.CompoundButton.OnCheckedChangeListener; 27 | import android.widget.EditText; 28 | import android.widget.TextView; 29 | 30 | @SuppressLint("InlinedApi") 31 | public class MainActivity extends Activity { 32 | private final static String LOG_TAG = "com.example.testkey"; 33 | Context mContext; 34 | TextView mTextView; 35 | private CheckBox mCheckBox = null; 36 | public static boolean mIsEnableDSuspend = false; 37 | private final static int[] time = {10, 120, 125}; 38 | private EditText connectTimeEditText; 39 | private EditText disconnectTimeEditText; 40 | private EditText disableTimeEditText; 41 | private String timeFile; 42 | private Button mButton; 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | setContentView(R.layout.activity_main); 48 | Log.d(LOG_TAG, "onCreate"); 49 | mContext = this; 50 | displayMyself(mContext); 51 | mTextView = (TextView)findViewById(R.id.textView); 52 | 53 | IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 54 | filter.addAction(Intent.ACTION_SCREEN_ON); 55 | filter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED); 56 | // Power 57 | registerReceiver(mBatInfoReceiver, filter); 58 | // Home 59 | final IntentFilter homeFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 60 | registerReceiver(homePressReceiver, homeFilter); 61 | 62 | connectTimeEditText = (EditText) this.findViewById(R.id.connected_time); 63 | disconnectTimeEditText = (EditText) this.findViewById(R.id.disconnected_time); 64 | disableTimeEditText = (EditText) this.findViewById(R.id.disable_time); 65 | mButton = (Button) this.findViewById(R.id.edit_button); 66 | mButton.setText("编辑"); 67 | 68 | timeFile = "time.txt"; 69 | printToast("开始测试按键!"); 70 | 71 | mCheckBox = (CheckBox) this.findViewById(R.id.enable_dsuspend_checkbox); 72 | mCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { 73 | @Override 74 | public void onCheckedChanged(CompoundButton buttonView, 75 | boolean isChecked) { 76 | mIsEnableDSuspend = isChecked; 77 | Intent intent = new Intent("android.intent.action.UPDATE_SUSPEND_TIME_BY_HAND"); 78 | sendBroadcast(intent); 79 | } 80 | }); 81 | 82 | // for service 83 | ((AudioManager)getSystemService(AUDIO_SERVICE)).registerMediaButtonEventReceiver(new ComponentName( 84 | this, 85 | MusicIntentReceiver.class)); 86 | 87 | // check intent 88 | Intent intent = getIntent(); 89 | if (intent != null) { 90 | String act = getIntent().getAction(); 91 | if (act != null) { 92 | if (act.equals(Intent.ACTION_VOICE_COMMAND)) { 93 | // voice command 94 | Log.d(LOG_TAG, "VOICE_COMMAND"); 95 | printToast("get Key VOICE_COMMAND"); 96 | } else if (act.equals("android.intent.action.CALL_PRIVILEGED") 97 | || act.equals("android.intent.action.KANGEAR_LASTREDIAL_TO_VR")) { 98 | printToast("LAST_NUMBER_REDIAL(需要重定向:" + KeyService.isNeedRedirect(this) +")"); 99 | if(KeyService.isNeedRedirect(this)) { 100 | startVoiceDial(); 101 | displayMyself(mContext); 102 | finish(); 103 | } else { 104 | printToast("DIAL_CUSTOM_NUMBER"); 105 | dail(intent); 106 | displayMyself(mContext); 107 | finish(); 108 | } 109 | } else { 110 | showMyself(mContext); 111 | } 112 | } 113 | } 114 | 115 | String content = getConfig(timeFile); 116 | if(content != null) { 117 | String ss[] = content.split(" "); 118 | if(ss != null && ss.length >= 3) { 119 | time[0] = Integer.valueOf(ss[0]); 120 | time[1] = Integer.valueOf(ss[1]); 121 | time[2] = Integer.valueOf(ss[2]); 122 | } 123 | } 124 | connectTimeEditText.setText(String.valueOf(time[0])); 125 | disconnectTimeEditText.setText(String.valueOf(time[1])); 126 | disableTimeEditText.setText(String.valueOf(time[2])); 127 | setEditTextEnable(false); 128 | } 129 | 130 | void updateConfig(String filename, String content) throws IOException { 131 | File file = new File(filename); 132 | file.delete(); 133 | FileOutputStream outStream = openFileOutput(filename, Context.MODE_PRIVATE); 134 | Log.d("LOG_TAG", "write bytes:" + content.getBytes().length); 135 | outStream.write(content.getBytes()); 136 | outStream.close(); 137 | } 138 | 139 | String getConfig(String filename) { 140 | byte[] byteName = new byte[20]; 141 | FileInputStream in; 142 | String ret = null; 143 | try { 144 | in = openFileInput(filename); 145 | int len = in.read(byteName); 146 | in.close(); 147 | ret = new String(byteName, 0, len, "UTF-8"); 148 | } catch (Exception e) { 149 | e.printStackTrace(); 150 | } 151 | Log.d(LOG_TAG, "str:" + ret); 152 | return ret; 153 | } 154 | 155 | public static boolean isEnableDSuspend() { 156 | return mIsEnableDSuspend; 157 | } 158 | 159 | public static int[] getTime() { 160 | return time; 161 | } 162 | 163 | private void showMyself(Context context) { 164 | Window window=((Activity) context).getWindow(); 165 | WindowManager.LayoutParams wl = window.getAttributes(); 166 | wl.alpha=1.0f; 167 | window.setAttributes(wl); 168 | } 169 | 170 | private void displayMyself(Context context) { 171 | Window window=((Activity) context).getWindow(); 172 | WindowManager.LayoutParams wl = window.getAttributes(); 173 | wl.alpha=0.0f; 174 | window.setAttributes(wl); 175 | } 176 | 177 | private void startVoiceDial() { 178 | Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND); 179 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 180 | // Verify that the intent will resolve to an activity 181 | if (intent.resolveActivity(getPackageManager()) != null) { 182 | startActivity(intent); 183 | } else { 184 | Log.e(LOG_TAG, "没有发现语音拨号器!"); 185 | } 186 | } 187 | 188 | private void dail(Intent intent) { 189 | Intent intent2 = new Intent(Intent.ACTION_CALL, intent.getData()); 190 | // Verify that the intent will resolve to an activity 191 | if (intent2.resolveActivity(getPackageManager()) != null) { 192 | startActivity(intent2); 193 | } else { 194 | Log.e(LOG_TAG, "没有发现拨号器!"); 195 | } 196 | } 197 | 198 | public void onClick(View v) { 199 | switch(v.getId()) { 200 | case R.id.edit_button: 201 | if(mButton.getText().toString().equals("编辑")) { 202 | mButton.setText("保存"); 203 | setEditTextEnable(true); 204 | } else { 205 | mButton.setText("编辑"); 206 | setEditTextEnable(false); 207 | try { 208 | time[0] = Integer.valueOf(connectTimeEditText.getText().toString()); 209 | time[1] = Integer.valueOf(disconnectTimeEditText.getText().toString()); 210 | time[2] = Integer.valueOf(disableTimeEditText.getText().toString()); 211 | updateConfig(timeFile, time[0] + " " 212 | + time[1] + " " + time[2]); 213 | } catch (Exception e) { 214 | e.printStackTrace(); 215 | } 216 | } 217 | } 218 | } 219 | 220 | void setEditTextEnable(boolean isEnable) { 221 | connectTimeEditText.setEnabled(isEnable); 222 | disconnectTimeEditText.setEnabled(isEnable); 223 | disableTimeEditText.setEnabled(isEnable); 224 | } 225 | 226 | @Override 227 | public void onDestroy() { 228 | 229 | // 解除注册 Power 230 | if (mBatInfoReceiver != null) { 231 | try { 232 | unregisterReceiver(mBatInfoReceiver); 233 | } catch (Exception e) { 234 | Log.e(LOG_TAG, "unregisterReceiver mBatInfoReceiver failure :" 235 | + e.getCause()); 236 | } 237 | }; 238 | // 解除注册 Home 239 | if (homePressReceiver != null) { 240 | try { 241 | unregisterReceiver(homePressReceiver); 242 | } catch (Exception e) { 243 | Log.e(LOG_TAG,"unregisterReceiver homePressReceiver failure :" 244 | + e.getCause()); 245 | } 246 | }; 247 | 248 | super.onDestroy(); 249 | } 250 | 251 | @Override 252 | public boolean onKeyDown(int keyCode, KeyEvent event) { 253 | printToast(KeyService.parseKeyCode(keyCode)); 254 | return true; 255 | //return super.onKeyDown(keyCode, event); 256 | } 257 | 258 | public void printToast(String str) { 259 | mTextView.setText(str); 260 | Log.i(LOG_TAG, str); 261 | } 262 | 263 | private final BroadcastReceiver homePressReceiver = new BroadcastReceiver() { 264 | final String SYSTEM_DIALOG_REASON_KEY = "reason"; 265 | final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey"; 266 | 267 | @Override 268 | public void onReceive(Context context, Intent intent) { 269 | String action = intent.getAction(); 270 | if (action.equals(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)) { 271 | String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); 272 | if (reason != null 273 | && reason.equals(SYSTEM_DIALOG_REASON_HOME_KEY)) { 274 | // 自己随意控制程序,关闭... 275 | } 276 | } 277 | } 278 | 279 | }; 280 | private final BroadcastReceiver mBatInfoReceiver = new BroadcastReceiver() { 281 | @Override 282 | public void onReceive(final Context context, final Intent intent) { 283 | 284 | final String action = intent.getAction(); 285 | if (Intent.ACTION_SCREEN_OFF.equals(action)) { 286 | printToast("get Key KEYCODE_POWER(KeyCode:26)-OFF"); 287 | } else if(Intent.ACTION_SCREEN_ON.equals(action)){ 288 | printToast("get Key KEYCODE_POWER(KeyCode:26)-NO"); 289 | } 290 | } 291 | }; 292 | } 293 | -------------------------------------------------------------------------------- /src/com/example/testkey/MusicIntentReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.testkey; 18 | 19 | import android.annotation.SuppressLint; 20 | import android.bluetooth.BluetoothAdapter; 21 | import android.content.BroadcastReceiver; 22 | import android.content.Context; 23 | import android.content.Intent; 24 | import android.util.Log; 25 | import android.view.KeyEvent; 26 | import android.widget.Toast; 27 | 28 | /** 29 | * Receives broadcasted intents. In particular, we are interested in the 30 | * android.media.AUDIO_BECOMING_NOISY and android.intent.action.MEDIA_BUTTON 31 | * intents, which is broadcast, for example, when the user disconnects the 32 | * headphones. This class works because we are declaring it in a 33 | * <receiver> tag in AndroidManifest.xml. 34 | */ 35 | @SuppressLint("InlinedApi") 36 | public class MusicIntentReceiver extends BroadcastReceiver { 37 | private static final String LOG_TAG = "MusicIntentReceiver"; 38 | private Context mContext; 39 | private KeyService mKeyService; 40 | 41 | @Override 42 | public void onReceive(Context context, Intent intent) { 43 | mContext = context; 44 | mKeyService = new KeyService(mContext); 45 | if (intent.getAction().equals( 46 | android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { 47 | Toast.makeText(context, "Headphones disconnected.", 48 | Toast.LENGTH_SHORT).show(); 49 | 50 | // send an intent to our MusicService to telling it to pause the 51 | // audio 52 | // context.startService(new Intent(MusicService.ACTION_PAUSE)); 53 | 54 | } else if (intent.getAction().equals(Intent.ACTION_MEDIA_BUTTON)) { 55 | Log.i(LOG_TAG, "ACTION_MEDIA_BUTTON!"); 56 | KeyEvent keyEvent = (KeyEvent) intent.getExtras().get( 57 | Intent.EXTRA_KEY_EVENT); 58 | if (keyEvent.getAction() != KeyEvent.ACTION_DOWN) 59 | return; 60 | 61 | switch (keyEvent.getKeyCode()) { 62 | case KeyEvent.KEYCODE_HEADSETHOOK: 63 | case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 64 | // context.startService(new 65 | // Intent(MusicService.ACTION_TOGGLE_PLAYBACK)); 66 | break; 67 | case KeyEvent.KEYCODE_MEDIA_PLAY: 68 | // context.startService(new Intent(MusicService.ACTION_PLAY)); 69 | break; 70 | case KeyEvent.KEYCODE_MEDIA_PAUSE: 71 | // context.startService(new Intent(MusicService.ACTION_PAUSE)); 72 | break; 73 | case KeyEvent.KEYCODE_MEDIA_STOP: 74 | // context.startService(new Intent(MusicService.ACTION_STOP)); 75 | break; 76 | case KeyEvent.KEYCODE_MEDIA_NEXT: 77 | // context.startService(new Intent(MusicService.ACTION_SKIP)); 78 | break; 79 | case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 80 | // TODO: ensure that doing this in rapid succession actually 81 | // plays the 82 | // previous song 83 | // context.startService(new Intent(MusicService.ACTION_REWIND)); 84 | break; 85 | } 86 | } else if (intent.getAction().equals( 87 | BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { 88 | Log.i(LOG_TAG, "BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED"); 89 | 90 | int state = intent.getIntExtra( 91 | BluetoothAdapter.EXTRA_CONNECTION_STATE, 0); 92 | 93 | if (state == BluetoothAdapter.STATE_CONNECTED 94 | || state == BluetoothAdapter.STATE_DISCONNECTED) { 95 | updateTime(); 96 | } 97 | } else if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) { 98 | Log.i(LOG_TAG, "Intent.ACTION_BOOT_COMPLETED"); 99 | updateTime(); 100 | } else if (intent.getAction().equals("android.intent.action.UPDATE_SUSPEND_TIME_BY_HAND")) { 101 | Log.i(LOG_TAG, "Intent.UPDATE_SUSPEND_TIME_BY_HAND"); 102 | updateTime(); 103 | } else { 104 | Log.i(LOG_TAG, "other intent"); 105 | } 106 | } 107 | 108 | void updateTime() { 109 | Log.i(LOG_TAG, "updateTime"); 110 | if(MainActivity.isEnableDSuspend()) { 111 | if(mKeyService.isBtConect()) 112 | mKeyService.setScreenOffTime(MainActivity.getTime()[0]*1000); // def secs 113 | else 114 | mKeyService.setScreenOffTime(MainActivity.getTime()[1]*1000); // def secs 115 | } else { 116 | mKeyService.setScreenOffTime(MainActivity.getTime()[2]*1000); // def secs 117 | } 118 | } 119 | } 120 | --------------------------------------------------------------------------------