寫個程式,‘監視’你的手機!

我是綠色大米呀發表於2019-03-29

最近遇到這樣的一個需求,獲取使用者當前正在使用的App。

在Android5.0之前,使用下面的程式碼即可獲得相關資訊:

        //5.0及以下
        private fun getTopAppOld(context: Context) {
            val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val topActivity = activityManager.getRunningTasks(1)[0].topActivity.packageName
            (context as TopAppInfoService).showToast(TopAppInfoService.getAppName(context, topActivity) + ":" + topActivity)
        }
複製程式碼

但是5.0以後此方法就不行了,需要使用UsageStatsManager應用使用資料統計服務。

使用UsageStatsManager應用使用資料統計服務需要使用者手動授權。

startActivityForResult(
    new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS),MY_PERMISSIONS_REQUEST_PACKAGE_USAGE_STATS);
複製程式碼

同時需要在註冊檔案中註冊許可權

<uses-permission
        android:name="android.permission.PACKAGE_USAGE_STATS"
        tools:ignore="ProtectedPermissions"/>
複製程式碼

我們可以同下面的方法判斷是否取得許可權:

//檢測使用者是否對本app開啟了“Apps with usage access”許可權
    private boolean hasPermission() {
        AppOpsManager appOps = (AppOpsManager)
                getSystemService(Context.APP_OPS_SERVICE);
        int mode = 0;
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) {
            mode = appOps.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,
                    android.os.Process.myUid(), getPackageName());
        }
        return mode == AppOpsManager.MODE_ALLOWED;
    }
複製程式碼

取得許可權之後,就可以啟動一個後的service來監聽當前使用者使用的App。

import android.app.ActivityManager
import android.app.Service
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import android.util.Log
import android.widget.Toast
import java.util.concurrent.TimeUnit

class TopAppInfoService : Service() {
    private var myThread: MyThread? = null

    class MyThread constructor(private val context: Context) : Thread() {
        private var isRun = true

        fun setStop() {
            isRun = false
        }

        override fun run() {
            while (isRun) {
                try {
                    TimeUnit.SECONDS.sleep(2)
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        getTopAppNew(context)
                    } else {
                        getTopAppOld(context)
                    }
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }

            }
        }

        //5.0及以下
        private fun getTopAppOld(context: Context) {
            val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val topActivity = activityManager.getRunningTasks(1)[0].topActivity.packageName
            (context as TopAppInfoService).showToast(TopAppInfoService.getAppName(context, topActivity) + ":" + topActivity)
        }

        //5.0及以上
        private fun getTopAppNew(context: Context) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                val m = context.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
                if (m != null) {
                    val now = System.currentTimeMillis()
                    //獲取60秒之內的應用資料
                    val stats = m.queryUsageStats(UsageStatsManager.INTERVAL_BEST, now - 60 * 1000, now)
                    Log.i(TAG, "Running app number in last 60 seconds : " + stats!!.size)

                    var topActivity = ""

                    //取得最近執行的一個app,即當前執行的app
                    if (stats != null && !stats.isEmpty()) {
                        var j = 0
                        for (i in stats.indices) {
                            if (stats[i].lastTimeUsed > stats[j].lastTimeUsed) {
                                j = i
                            }
                        }
                        topActivity = stats[j].packageName//包名
                    }
                    if (!topActivity.isEmpty()) {
                        (context as TopAppInfoService).showToast(TopAppInfoService.getAppName(context, topActivity) + ":" + topActivity)
                    }
                }
            }
        }
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        myThread = MyThread(this)
        myThread!!.start()
        Log.i(TAG, "Service is start.")
    }

    override fun onDestroy() {
        super.onDestroy()
        myThread!!.setStop()
        Log.i(TAG, "Service is stop.")
    }

    fun showToast(txt: String?) {
        if (txt == null) {
            Log.e(TAG, "call method showToast, text is null.")
            return
        }
        val handler = Handler(Looper.getMainLooper())

        handler.post {
            Toast.makeText(this@TopAppInfoService, txt, Toast.LENGTH_LONG)
                    .show()
        }
    }

    companion object {
        val TAG = "TopAppInfoService"

        fun getAppName(context: Context, packageName: String): String {
            val pm = context.packageManager
            var Name: String
            try {
                Name = pm.getApplicationLabel(pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA)).toString()
            } catch (e: PackageManager.NameNotFoundException) {
                Name = ""
            }

            return Name
        }
    }
}
複製程式碼

上面對Android5.0做了相容,可以拿到App的包名和應用名。

剩下的就是在適當的時候啟動服務了:

 btnStart = (Button) this.findViewById(R.id.btnStart);
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, TopAppInfoService.class);
                startService(intent);
            }
        });
        btnStop = (Button) this.findViewById(R.id.btnStop);
        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, TopAppInfoService.class);
                stopService(intent);
            }
        });
複製程式碼

在華為,小米和一加的真機上通過測試,請放心食用。

祝你生活愉快。

相關文章