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

asuschb 8年前
   <p>我们平常知道的大多都是Android温柔、方便、易操作等特性。但是他也有霸道总裁的一面咯,只是你不知道罢了~~~</p>    <p>下面我们来说说Android的霸道总裁一面,其实因为是系统是开源的,所以我们直接可以翻阅源码,做一些霸道级操作,比如让你的手机装上我这个App之后被控制,状态栏无法下拉,无法卸载,且只能使用规定的应用,是不是很霸(流)道(氓)啊...</p>    <p>好了 我们下面就说说怎么控制你的手机呢。</p>    <h2>1. 霸道之路之状态栏无法下拉</h2>    <pre>  <code class="language-java">/**   * 指的是这个Activity得到或者失去焦点的时候 就会call   * @param hasFocus   */  @Override  public void onWindowFocusChanged(boolean hasFocus) {      if (!hasFocus && isControlStatusBarEnable) {          //收起状态栏          disableStatusBar();      }  }</code></pre>    <p>拉下状态栏的时候,显示的Activity就失去焦点,这就是我们的思路。根据焦点是不是在此Activity来判断是否拉下状态栏</p>    <pre>  <code class="language-java">/**   *通过反射拿到获取收起状态栏的方法并执行   */  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();      }  }</code></pre>    <p>首先通过反射拿到SystemService中的 statusbar,然后又通过反射拿到statusBar管理类StatusBarManager类中的collapse()方法,并且执行此方法。关于Method中的invoke(),主要是为了类反射,这样你可以在不知道具体的类的情况下,根据配置的字符串去调用一个类的方法。在灵活编程的时候非常有用。 </p>    <p>2. 霸道之路之实现App的表面无法卸载,且如果"非法"卸载直接锁死手机,解锁密码你说了算哦~ 兴不兴奋,霸(流)不霸(流)道(氓)。</p>    <p>首先我们要知道怎么才能让App装上之后一般无法卸载呢,其实就是激活此App。思路有了代码撸起~</p>    <pre>  <code class="language-java">/**   * 跳至设备管理器激活此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);  }</code></pre>    <p>上面的代码我们具体就是进入系统的设备管理器,大家注意下 <strong>DeviceManagerReciver.class</strong> 这个类。我们随后再说。我们进入设备管理器之后,可是怎么才能用户到底有没有点击激活按钮才回到此App中呢,这个我们怎么判断呢。别慌你看看我们上面代码的最后一行跳转的时候我们用的是 <strong>startActivityForResult(intent, requestCode);</strong> 所以我们只需要这样子</p>    <pre>  <code class="language-java">@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();      }  }</code></pre>    <p>好了  ,我们再来说说刚才我们提到的 <strong>DeviceManagerReciver.class</strong> 这个类。它是继承自 <strong>DeviceAdminReceiver,</strong> 在这个类中,我们要做的就是刚才吹下的牛B。一般情况下,用户是无法卸载我们的App,但是就像我们刚才一样用户找到设备管理器,取消激活此App,那不就可以轻易的卸载我们这个霸道的App了么( 那还了得,怎么可能让用户想干什么就干什么呢,哼~ )。所以,为了杜绝这一bug,我们直接更改手机密码不就行了嘛(-.-)。</p>    <pre>  <code class="language-java">/**   * 取消激活的时候调用   * @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();  }  </code></pre>    <p>假如用户进入设备管理器,取消激活这个App,就执行 <strong>onDisableRequested</strong> 这个方法,锁定手机,更改密码的操作就放在这个方法里面操作。</p>    <p>切记 ~~~ 我们必须在清单文件manifest里面注册这个广播</p>    <pre>  <code class="language-java"><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></code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/df859125254d23983ae9e5184e91c2ef.png"></p>    <p>清单文件中的meta-data</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7ca8170f96e7bf5bd6d3393fc7da8642.png"></p>    <p>在res下建立xml文件夹,在此目录下创建manifests 下注册的文件名,代码如下:</p>    <pre>  <code class="language-java"><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></code></pre>    <h2>3. 霸道之路之只访问规定的应用</h2>    <p>只能访问你规定的应用,这个是不是更霸(流)道(氓)了。管他的,代码撸起~</p>    <p>首先我们要获取手机里面所有的应用</p>    <pre>  <code class="language-java">/**   * 获取手机里面所有的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();  }</code></pre>    <p>接着我们只需要判断当前应用是否在制定应用范围内</p>    <pre>  <code class="language-java">/**       * 判断当前应用是否在制定应用范围内       *       * @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;      }</code></pre>    <p>下面我只需要开启服务,做一个任务计划,在后台间隔一段时间判断是否在此App或者在制定的app中,如果不是,则立即进入此app。</p>    <pre>  <code class="language-java">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();          }      }  }  </code></pre>    <p>这就是我在项目中用到的霸道总裁模式。其实这几个知识点,都是冷知识。一般的项目肯定用不到这些,因为一般的App肯定是用户怎么方便怎么来的,不会去刻意的限制一些系统的功能。虽然几乎不可能用到,但是对于我们这些程序员来说,多了解一点总归不是坏事。好像我这篇就是再让大家使坏呢 ヾ(o◕∀◕)ノヾ</p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/06063c226ba0</p>    <p> </p>