Android 你不知道的霸道总裁模式

我们平常知道的大多都是Android温柔、方便、易操作等特性。但是他也有霸道总裁的一面咯,只是你不知道罢了~~~

下面我们来说说Android的霸道总裁一面,其实因为是系统是开源的,所以我们直接可以翻阅源码,做一些霸道级操作,比如让你的手机装上我这个App之后被控制,状态栏无法下拉,无法卸载,且只能使用规定的应用,是不是很霸(流)道(氓)啊...

好了 我们下面就说说怎么控制你的手机呢。

1. 霸道之路之状态栏无法下拉

/**
* 指的是这个Activity得到或者失去焦点的时候 就会call
* @param hasFocus
*/
@Override
public void onWindowFocusChanged(boolean hasFocus) {
if (!hasFocus && isControlStatusBarEnable) {
//收起状态栏
disableStatusBar();
}
}

拉下状态栏的时候,显示的Activity就失去焦点,这就是我们的思路。根据焦点是不是在此Activity来判断是否拉下状态栏

/**
*通过反射拿到获取收起状态栏的方法并执行
*/
public void disableStatusBar() {
try {
Object service = getSystemService("statusbar");
//通过反射获取StatusBarManager
Class<?> claz = Class.forName("android.app.StatusBarManager");
//获取StatusBarManager类中的collapse()方法
Method expand = claz.getMethod("collapse");
expand.invoke(service);
} catch (Exception e) {
e.printStackTrace();
}
}

首先通过反射拿到SystemService中的 statusbar,然后又通过反射拿到statusBar管理类StatusBarManager类中的collapse()方法,并且执行此方法。关于Method中的invoke(),主要是为了类反射,这样你可以在不知道具体的类的情况下,根据配置的字符串去调用一个类的方法。在灵活编程的时候非常有用。invoke方法详解请猛戳此处

2. 霸道之路之实现App的表面无法卸载,且如果"非法"卸载直接锁死手机,解锁密码你说了算哦~ 兴不兴奋,霸(流)不霸(流)道(氓)。

首先我们要知道怎么才能让App装上之后一般无法卸载呢,其实就是激活此App。思路有了代码撸起~

/**
* 跳至设备管理器激活此App,一般情况无法直接卸载App,除非进入设备管理器取消激活此App
*/
private void openDeviceManager(int requestCode){
//DeviceManagerReciver.class 在manifest里面注册
ComponentName componentName = new ComponentName(this, DeviceManagerReciver.class);
//添加一个隐式意图,完成设备权限的添加
//这个Intent (DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN)跳转到 权限提醒页面
//并传递了两个参数EXTRA_DEVICE_ADMIN 、 EXTRA_ADD_EXPLANATION

Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
//权限列表
//EXTRA_DEVICE_ADMIN参数中说明了用到哪些权限,
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, componentName);
//描述(additional explanation)
//EXTRA_ADD_EXPLANATION参数为附加的说明
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "您可千万别激活我啊~~~~");
startActivityForResult(intent, requestCode);
}

上面的代码我们具体就是进入系统的设备管理器,大家注意下DeviceManagerReciver.class这个类。我们随后再说。我们进入设备管理器之后,可是怎么才能用户到底有没有点击激活按钮才回到此App中呢,这个我们怎么判断呢。别慌你看看我们上面代码的最后一行跳转的时候我们用的是startActivityForResult(intent, requestCode); 所以我们只需要这样子

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode==1&&resultCode ==RESULT_OK){
//在此处做你想做的操作
isActivate = true;
Toast.makeText(this,"您已成功激活此App,并且无法卸载此App,手机密码为:1234,请牢记密码为1234,",Toast.LENGTH_LONG).show();
Toast.makeText(this,"重要的事情说三遍,请牢记密码为1234",Toast.LENGTH_LONG).show();
}
}

好了  ,我们再来说说刚才我们提到的DeviceManagerReciver.class这个类。它是继承自DeviceAdminReceiver,在这个类中,我们要做的就是刚才吹下的牛B。一般情况下,用户是无法卸载我们的App,但是就像我们刚才一样用户找到设备管理器,取消激活此App,那不就可以轻易的卸载我们这个霸道的App了么( 那还了得,怎么可能让用户想干什么就干什么呢,哼~ )。所以,为了杜绝这一bug,我们直接更改手机密码不就行了嘛(-.-)。

/**
* 取消激活的时候调用
* @param context
* @param intent
* @return 取消激活时 弹框的显示文本
*/
@Override
public CharSequence onDisableRequested(Context context, Intent intent) {
DevicePolicyManager manager = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName name = new ComponentName(context, DeviceManagerReciver.class);
//代表实际是否被激活
boolean adminActive = manager.isAdminActive(name);
if (adminActive){
//立刻锁定手机
manager.lockNow();
//重置手机密码
manager.resetPassword("1234",0);
}else {
// 指定动作名称
intent.setAction(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
// 指定给哪个组件授权
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, name);
context.startActivity(intent);
}
return "取消激活或影响程序的正常使用";
}



/**
* 重置密码成功回调
* @param context
* @param intent
*/
@Override
public void onPasswordSucceeded(Context context, Intent intent) {
super.onPasswordSucceeded(context, intent);
Toast.makeText(context,"密码更改成功!新密码:1234",Toast.LENGTH_LONG).show();
}

/**
* 重置密码失败回调
* @param context
* @param intent
*/
@Override
public void onPasswordFailed(Context context, Intent intent) {
super.onPasswordFailed(context, intent);
Toast.makeText(context,"密码更改失败",Toast.LENGTH_LONG).show();
}

假如用户进入设备管理器,取消激活这个App,就执行onDisableRequested这个方法,锁定手机,更改密码的操作就放在这个方法里面操作。

切记 ~~~ 我们必须在清单文件manifest里面注册这个广播

<receiver
android:name=".DeviceManagerReciver"
android:label="@string/app_name"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_admin"/>
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
<action android:name="android.intent.action.BOOT_COMPLETED"/>

<category android:name="android.intent.category.HOME"/>
</intent-filter>
</receiver>


清单文件中的meta-data


在res下建立xml文件夹,在此目录下创建manifests 下注册的文件名,代码如下:

<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
<uses-policies>
<limit-password />
<!--监控登录尝试-->
<watch-login />
<!--重置密码-->
<reset-password />
<!--强行锁定-->
<force-lock />
<!--清除所有数据 回复出厂设置-->
<wipe-data />
<expire-password />
<encrypted-storage />
<disable-camera />
<disable-keyguard-features />
</uses-policies>
</device-admin>

3. 霸道之路之只访问规定的应用

只能访问你规定的应用,这个是不是更霸(流)道(氓)了。管他的,代码撸起~

首先我们要获取手机里面所有的应用

/**
* 获取手机里面所有的App
*/
private void getAllApp() {
PackageManager manager = getApplicationContext().getPackageManager();
Intent intent = new Intent();
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
//获取并添加系统所有的app
List<ResolveInfo> list = manager.queryIntentActivities(intent, 0);

for (int i = 0; i < list.size(); i++) {
AppEntity entity = new AppEntity(Parcel.obtain());
ActivityInfo info = list.get(i).activityInfo;
Drawable drawable = info.loadIcon(manager);
String name = info.loadLabel(manager).toString();
String packageName = info.packageName;
entity.appName = name;
entity.packageName = packageName;
entity.drawable = drawable;
entities.add(entity);
}
adapter.notifyDataSetChanged();
}

接着我们只需要判断当前应用是否在制定应用范围内

/**
* 判断当前应用是否在制定应用范围内
*
* @return
*/
private boolean isIncludedApp() {
if (isHome()) {
return false;
}
try {
StringBuffer sd = new StringBuffer("");
sd.append("com.walle.control");
for (int i = 0; i < entities.size(); i++) {
AppEntity conAppEntity = entities.get(i);
sd.append(conAppEntity.packageName);
}
String sdString = sd.toString();
ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
// ToastUtil.showShortToast(getApplicationContext(), rti.get(0).topActivity.getPackageName());
return sdString.contains(rti.get(0).topActivity.getPackageName());
} catch (Exception e) {
return false;
}
}

/**
* 判断当前界面是否是桌面
*
* @return
*/
private boolean isHome() {
ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
return getHomes().contains(rti.get(0).topActivity.getPackageName());

}

/**
* 获取属于桌面的应用的应用包名称
*
* @return 返回包含所有包名的字符串列表
*/
private List<String> getHomes() {
List<String> names = new ArrayList<String>();
PackageManager packageManager = this.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);

List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(
intent, packageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo ri : resolveInfos) {
names.add(ri.activityInfo.packageName);
}
return names;
}

下面我只需要开启服务,做一个任务计划,在后台间隔一段时间判断是否在此App或者在制定的app中,如果不是,则立即进入此app。

class AppTimerTask extends TimerTask {

@Override
public void run() {
if (MainActivity.isControl) {
if (!isIncludedApp()) {
//如果不是在此应用或者指定的App,就立刻跳入此App
Intent intent = new Intent(getApplicationContext(),
MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}else {
if (timer!=null)timer.cancel();
if (appTimerTask!=null) appTimerTask.cancel();
}
}
}

这就是我在项目中用到的霸道总裁模式。其实这几个知识点,都是冷知识。一般的项目肯定用不到这些,因为一般的App肯定是用户怎么方便怎么来的,不会去刻意的限制一些系统的功能。虽然几乎不可能用到,但是对于我们这些程序员来说,多了解一点总归不是坏事。好像我这篇就是再让大家使坏呢 ヾ(o◕∀◕)ノヾ

代码已上传至Github

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,465评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,598评论 18 139
  • 在一个移动操作系统中,APP可以实现一个叫做消息推送(push)的功能。push是能够起到提醒或者唤醒用户的作用的...
    产品蔡老板阅读 112,681评论 23 366
  • 大概在某年某月某日的日记中记录过。 你从那个有海的城市过来,静静站在我身后。我回头看见你浅浅地笑。 然后你说,我们...
    岩盐芝士奶盖的岩盐阅读 249评论 0 0
  • 文/教主 来了,缘聚,他走了,缘散;你找他,缘起,你不找他,缘灭;找到是缘起,找不到是缘尽;走过的路,见过的人,各...
    隔壁村的教主阅读 819评论 0 4