06.输入系统:第10课第11节_输入系统_实战_使用GlobalKey一键启动程序_P
该课时开始讲解GlobalKey一键启动程序,一键启动的过程如下:
a.对于GlobalKey,系统会根据global_key.xml文件决定发送消息给那个组件
b.APP应该注册广播消息的接收者
1.编写一个BroadCastReceiver派生类,实现消息的处理函数
2.注册派生类
c.在该组件中启动APP
首先我们实现a,b两点,先写出一个能接收广播消息的应用程序,、
APP应用程序
我们先来实现上述的b,然后再实现a。
APP修改
我们在之前的APP_0001_LEDDemp-V3上进行修改,使用AS进入该工程,点击:
弹出如下窗口:
给需要创建的类命名为MyBroadcastReceiver,可以看到生成了一个新的文件,其内容为下:
public class MyBroadcastReceiver {
}
我们让该类继承于BroadcastReceiver,然后我们需要复写其成员onReceive,简单修改如下:
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context,"Get BroadcasstReceiver",Toast.LENGTH_SHORT).show();
}
}
当接收到广播的时候会执行onReceive函数,Toast.makeText(context,“Get BroadcasstReceiver”,Toast.LENGTH_SHORT).show()为简单的显示一个提示框,其内容为"Get BroadcasstReceiver",下面我们修改app-> manifests-> AndroidManifest.xml文件,注册该APP为一个静态广播接收者,在<application下方,添加代码如下:
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.GLOBAL_BUTTON"/>
</intent-filter>
</receiver>
其中的android:name="android.intent.action.GLOBAL_BUTTON"代表只有接收到GLOBAL_BUTTON这种广播,才会执行onReceive方法。
APP系统签名
发送一个ndroid.intent.action.GLOBAL_BUTTON的广播,是需要ACTION_MANAGE_WRITE_SETTINGS权限的, 在安卓6.0以后获取ACTION_MANAGE_WRITE_SETTINGS权限,需要通过用户进行确认,在onCreate中,我们添加如下获取权限代码:
checkBoxLed4 = (CheckBox)findViewById(R.id.LED4);
button.setOnClickListener(new MyButtonListener());
+ //申请android.permission.WRITE_SETTINGS权限的方式
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ //如果当前平台版本大于23平台
+ if (!Settings.System.canWrite(this)) {
+ //如果没有修改系统的权限这请求修改系统的权限
+ Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);
+ intent.setData(Uri.parse("package:" + getPackageName()));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivityForResult(intent, 0);
+ } else {
+ //有了权限,你要做什么呢?具体的动作
+ // processShow();
+ }
+ }
这样就能跳转到用户设置权限界面了,但是,我们运行APP的时候会发现,虽然跳转到了用户界面,选择的拖动图标,是灰色的,依然无法选择,因为在android 6.0及以后,WRITE_SETTINGS权限的保护等级已经由原来的dangerous升级为signature,这意味着我们的APP需要用系统签名或者成为系统预装软件才能够申请此权限,并且还需要提示用户跳转到修改系统的设置界面去授予此权限
那么下面我们就设置让我们的APP使用系统签名,获取系统权限,首先我们在linux中创建文件夹sigAPK,并且在该目录下创建文件keytool-importkeypair,编写内容如下(该为一个脚本文件):
#! /bin/bash
#
# This file is part of keytool-importkeypair.
#
# keytool-importkeypair is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 3 of
# the License, or (at your option) any later version.
#
# keytool-importkeypair is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with keytool-importkeypair. If not, see
# <http://www.gnu.org/licenses/>.
#
DEFAULT_KEYSTORE=$HOME/.keystore
keystore=$DEFAULT_KEYSTORE
pk8=""
cert=""
alias=""
passphrase=""
tmpdir=""
scriptname=`basename $0`
usage() {
cat << EOF
usage: ${scriptname} [-k keystore] [-p storepass]
-pk8 pk8 -cert cert -alias key_alias
This script is used to import a key/certificate pair
into a Java keystore.
If a keystore is not specified then the key pair is imported into
~/.keystore in the user's home directory.
The passphrase can also be read from stdin.
EOF
}
cleanup() {
if [ ! -z "${tmpdir}" -a -d ${tmpdir} ]; then
rm -fr ${tmpdir}
fi
}
while [ $# -gt 0 ]; do
case $1
in
-p | --passphrase | -passphrase)
passphrase=$2
shift 2
;;
-h | --help)
usage
exit 0
;;
-k | -keystore | --keystore)
keystore=$2
shift 2
;;
-pk8 | --pk8 | -key | --key)
pk8=$2
shift 2
;;
-cert | --cert | -pem | --pem)
cert=$2
shift 2
;;
-a | -alias | --alias)
alias=$2
shift 2
;;
*)
echo "${scriptname}: Unknown option $1, exiting" 1>&2
usage
exit 1
;;
esac
done
if [ -z "${pk8}" -o -z "${cert}" -o -z "${alias}" ]; then
echo "${scriptname}: Missing option, exiting..." 1>&2
usage
exit 1
fi
for f in "${pk8}" "${cert}"; do
if [ ! -f "$f" ]; then
echo "${scriptname}: Can't find file $f, exiting..." 1>&2
exit 1
fi
done
if [ ! -f "${keystore}" ]; then
storedir=`dirname "${keystore}"`
if [ ! -d "${storedir}" -o ! -w "${storedir}" ]; then
echo "${scriptname}: Can't access ${storedir}, exiting..." 1>&2
exit 1
fi
fi
# Create temp directory ofr key and pkcs12 bundle
tmpdir=`mktemp -q -d "/tmp/${scriptname}.XXXX"`
if [ $? -ne 0 ]; then
echo "${scriptname}: Can't create temp directory, exiting..." 1>&2
exit 1
fi
key="${tmpdir}/key"
p12="${tmpdir}/p12"
if [ -z "${passphrase}" ]; then
# Request a passphrase
read -p "Enter a passphrase: " -s passphrase
echo ""
fi
# Convert PK8 to PEM KEY
openssl pkcs8 -inform DER -nocrypt -in "${pk8}" -out "${key}"
# Bundle CERT and KEY
openssl pkcs12 -export -in "${cert}" -inkey "${key}" -out "${p12}" -password pass:"${passphrase}" -name "${alias}"
# Print cert
echo -n "Importing \"${alias}\" with "
openssl x509 -noout -fingerprint -in "${cert}"
# Import P12 in Keystore
keytool -importkeystore -deststorepass "${passphrase}" -destkeystore "${keystore}" -srckeystore "${p12}" -srcstoretype PKCS12 -srcstorepass "${passphrase}"
# Cleanup
cleanup
编写完成之后,退出保存,然后拷贝build/target/product/security/目录下的platform.pk8,platform.x509.pem到自己创建的sigAPK目录中,然后在linux环境下执行
./keytool-importkeypair -k SignKitking.jks -p 123456 -pk8 platform.pk8 -cert platform.x509.pem -alias SignKitking
该命令的格式如下:
./keytool-importkeypair -k [jks文件名] -p [jks的密码] -pk8 platform.pk8 -cert platform.x509.pem -alias [jks的别名]
执行之后,我们可以看到生成SignKitking.jks文件,然后拷贝sigAPK目录到AS工程目录,如下:
该些工作完之后我们还需要配置AS工程,首先在AndroidManifest.xml文件中添加如下代码(与<application同级),为获取系统权限:
android:sharedUserId="android.uid.system" >
在build.gradle(Module:app)的android 下添加:
signingConfigs {
release {
storeFile file('C:/AndroidStudio/3.workProject/andriod7.1APP/APP6/APP_0001_LEDDemp-V3/sigAPK/SignKitking.jks')
storePassword '123456'
keyAlias 'SignKitking'
keyPassword '123456'
}
debug {
storeFile file('C:/AndroidStudio/3.workProject/andriod7.1APP/APP6/APP_0001_LEDDemp-V3/sigAPK/SignKitking.jks')
storePassword '123456'
keyAlias 'SignKitking'
keyPassword '123456'
}
}
该处的路基大家不要着急,暂时这样填写,然后选择:
进入:
选择Signing,然后重新配置路径,接下来下面的是重点,请大家注意:
选择BuildTypes之后,一定要配置SigningConfig为debug(一定要配置,一定要配置)。
以上所有动作完成之后,重新编译APP,如果编译出错,可以在build.gradle(Module:app)的android中添加:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
signingConfig signingConfigs.debug
}
}
当然,或许解决不了你的根本问题,那么可以另行百度,下面我们继续修改APP。
发送广播)
完成以上步骤之后,我们开启开发板,运行APP,在串口终端输入su,进入管理模式:首先运行一次(不知道具体原因):
am broadcast -a 1 -n com.example.administrator.app_0001_leddemp/.MyBroadcastReceiver
随便发送一个广播(或许此广播为错误广播),然后再发送GLOBAL_BUTTON广播(多次发送)
am broadcast -a android.intent.action.GLOBAL_BUTTON -n com.example.administrator.app_0001_leddemp/.MyBroadcastReceiver
我们可以看到开发板会出现黑色提示框
除了以上发送广播的方法,还可以在MainActivity.java文件的public void onClick(View view) 方法中,添加如下代码:
public void onClick(View view) {
button.setText("ALL OFF");
ledon = !ledon;
+ Intent intent = new Intent("android.intent.action.GLOBAL_BUTTON");
+ intent.putExtra("msg", "hello receiver.");
+ sendBroadcast(intent);
这样每次点击all按钮,就会发送一个广播,能看到接收到广播的提示。这样就证明我们可以接收到广播了(当程序退出的时候,也能接收到),下面我们在收到广播执行的函数MyBroadcastReceiver.onReceive方法中去实现启动APP。
启动APP
在MyBroadcastReceiver.onReceive方法中添加如下代码:
Intent intentNewTask =new Intent(context, MainActivity.class);
intentNewTask.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intentNewTask);
其中MainActivity.class就是要启动的应用程序,添加之后进行编译,然后在开发板上运行APP,启动之后我们再推出APP,然后执行:
am broadcast -a 1 -n com.example.administrator.app_0001_leddemp/.MyBroadcastReceiver
随便发送一个广播(或许此广播为错误广播),然后再发送GLOBAL_BUTTON广播(多次发送)
am broadcast -a android.intent.action.GLOBAL_BUTTON -n com.example.administrator.app_0001_leddemp/.MyBroadcastReceiver
这样我们可以看到APP自动启动,到此为止,应用程程序我们已经编写完成,即最开头的b步骤我们已经完成,下面倒过来完成a步骤。
底层修改
首先我们打开源码中的SDK\frameworks\base\core\res\res\xml\global_keys.xml文件,该文件原内容如下:
<global_keys version="1">
<!-- Example format: keyCode = keycode to handle globally. component = component which will handle this key. -->
<!-- <key keyCode="KEYCODE_VOLUME_UP" component="com.android.example.keys/.VolumeKeyHandler" /> -->
</global_keys>
其中前面的 <!-- 表示注释,我们添加以下内容:
<global_keys version="1">
<!-- Example format: keyCode = keycode to handle globally. component = component which will handle this key. -->
<!-- <key keyCode="KEYCODE_VOLUME_UP" component="com.android.example.keys/.VolumeKeyHandler" /> -->
+ <key keyCode="KEYCODE_TV" component="com.example.administrator.app_0001_leddemp/.MyBroadcastReceiver" />
</global_keys>
表示,当我们按下KEYCODE_TV按钮,就会发送广播给com.example.administrator.app_0001_leddemp/.MyBroadcastReceiver,然后启动应用程序。
修改完成之后,执行
mmm frameworks/base/core/res/编译,他会生成out/target/product/qytech_azalea/system/framework/framework-res.apk,
然后执行
make snod -j8
重新烧写system.img文件
启动开发板,安装app之后,在终端输入:
input keyevent 170
可以看到APP自动启动,其中上报170是因为在源码中:
field public static final int KEYCODE_TV = 170; // 0xaa
所以我们上报170,相当于按下了KEYCODE_TV按键,或者执行:
input keyevent TV
也能达到同样的效果