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

asuschb 7年前發布 | 7K 次閱讀 安卓開發 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(),主要是為了類反射,這樣你可以在不知道具體的類的情況下,根據配置的字符串去調用一個類的方法。在靈活編程的時候非常有用。 

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???)?ヾ

 

 

來自:http://www.jianshu.com/p/06063c226ba0

 

 本文由用戶 asuschb 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!