大致描述:公司的板子是淘宝买的,系统android6.0,有root权限,没有任何技术支持
需求:1 实现广告机导航栏隐藏以及禁止下滑,也就是说用户只能看见我的app,不能退出进入android系统桌面
2 开机自启,自动升级且不能让用户点击,也就是静默安装,而后自启动应用,
实现经历:由于没有技术支持,且个人只是android应用层开发,所以修改源码方案不现实,自己也没有这个能力,只能从应用方面解决,经过几天面向搜索编程,有如下解决方案。
需求1实现:wm命令修改屏幕的显示范围。(修改系统主题和window flag方案没有找到较好的实现方式,虚拟按键还是会弹出来)
adb shell wm overscan 0,-30,0,-168
如上:通过adb命令将屏幕上边距和下边距分别移除屏幕外,在屏幕外就无法触摸了,这样就不会下拉出菜单了,而虚拟按键也在屏幕外,也不用担心用户点击退出应用的问题了。
0,-30,0,-168对应左上右下,具体参数根据屏幕调整,也可以通过代码获取状态栏和虚拟按键高度调整。
进一步思考:如何实现
wm overscan 0,-30,0,-168 代码执行时
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />//需要加权限
代码如下:
object OverscanUtil {
/**
* 修改屏幕左上右下的margin距离
* left:20
* 相当于:android:layout_marginLeft="20px"
* @param left 距离真实屏幕左边距
* @param top 距离真实屏幕上边距
* @param right 距离真实屏幕右边距
* @param bottom 距离真实屏幕下边距
*/
fun changeOverscan(left:Int=0,top:Int=0,right:Int=0,bottom:Int=0){
val split = "wm overscan $left,$top,$right,$bottom".split(" ").toTypedArray()
try {
Runtime.getRuntime().exec(split)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
implementation 'com.blankj:utilcode:1.23.2' //依赖此工具类用于计算app状态栏高度和底部虚拟按键高度
监听进入app:执行一下代码隐藏底部虚拟按键,和状态栏。
val statusBarHeight = BarUtils.getStatusBarHeight()+10
val navBarHeight = BarUtils.getNavBarHeight()
OverscanUtil.changeOverscan(top = -statusBarHeight,bottom = -navBarHeight)
退出app,显示虚拟按键和状态栏
OverscanUtil.changeOverscan()
为了方便可以在debug模式不隐藏虚拟按键,也可以通过 adb shell wm overscan 0,0,0,0 还原。
需求2实现:静默安装与静默安装后自启动以及开机自启动
注意:当前版本和需要升级的版本apk最好都用release包,而且升级版本要高于当前版本,否则可能升级失败。
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
静默安装工具类,需要root权限
public class InstallUtils {
//com.xx.xx 需要被静默安装app包名 apkAbsolutePath安装的apk路径
public boolean silentInstall(String apkAbsolutePath)
{
String[] args =
{"pm", "install", "-i", "com.xx.xx", "-r",
apkAbsolutePath};
if (Build.VERSION.SDK_INT >= 24)
{
args = new String[]
{"pm", "install", "-r", "-i", "com.xx.xx", "--user",
"0", apkAbsolutePath};
}
String result = "EMPTY";
ProcessBuilder processBuilder = new ProcessBuilder(args);
Process process = null;
InputStream inIs = null;
try
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int read = -1;
process = processBuilder.start();
baos.write('/');
inIs = process.getInputStream();
while ((read = inIs.read()) != -1)
{
baos.write(read);
}
byte[] data = baos.toByteArray();
result = new String(data, StandardCharsets.UTF_8);
Log.e(TAG, "resultString-------->>>" + result);
baos.close();
} catch (IOException e)
{
Log.e(TAG, "静默安装无效!!!");
} finally
{
try
{
if (inIs != null)
{
inIs.close();
}
} catch (IOException e)
{
}
if (process != null)
{
process.destroy();
}
}
return result.contains("Success");
}
AndroidManifest.xml添加如下广播:
<receiver
android:name=".SelfStartReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</receiver>
<receiver
android:name=".ReplaceReceiver"
android:enabled="true"
android:exported="true">
<intent-filter android:priority="1000">
<action android:name="android.intent.action.PACKAGE_REPLACED"/>
<data android:scheme="package"/>
</intent-filter>
</receiver>
具体广播类:
class ReplaceReceiver:BroadcastReceiver(){
override fun onReceive(context: Context, intent: Intent) {
Log.e("ReplaceReceiver",intent.action)
if (intent.action=="android.intent.action.PACKAGE_REPLACED") {
val packageName = intent.dataString?.substring(8)
if (packageName == "com.xx.xx") {//这里是你的包名
startSelf(context)
}
}
}
private fun startSelf(context: Context) {
val i = Intent(context, MainActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
}
}
class SelfStartReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.e("SelfStartReceiver",intent.action)
if (intent.action == "android.intent.action.BOOT_COMPLETED") {
startSelf(context)
}
}
private fun startSelf(context: Context) {
val i = Intent(context, MainActivity::class.java)
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(i)
}
}
,注意,安装之前将你的apk放到system/priv-app目录也就是说低版本的apk
adb remount
adb push /你的路径/app-release.apk /system/priv-app
adb reboot //等待重启
然后调用此代码开始静默安装,最好子线程里
InstallUtils.silentInstall("/sdcard/yuehuiot/xxx.apk")//这里面填写需要更新apk版本