Last active
September 14, 2016 14:18
-
-
Save nosix/0f2a7f55ebcc39bc56ab9eccf7991414 to your computer and use it in GitHub Desktop.
How to use Android Service API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<manifest | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
package="xxx"> | |
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | |
<uses-permission | |
android:name="android.permission.PACKAGE_USAGE_STATS" | |
tools:ignore="ProtectedPermissions" /> | |
<application | |
android:allowBackup="true" | |
android:fullBackupContent="true" | |
android:icon="@mipmap/ic_launcher" | |
android:label="@string/app_name" | |
android:supportsRtl="true" | |
android:theme="@style/AppTheme"> | |
<activity android:name=".MainActivity"> | |
<intent-filter> | |
<action android:name="android.intent.action.MAIN" /> | |
<category android:name="android.intent.category.LAUNCHER" /> | |
</intent-filter> | |
</activity> | |
<service | |
android:name=".AppMonitorService" | |
android:exported="false" /> | |
<receiver | |
android:name=".StartupReceiver"> | |
<intent-filter> | |
<action android:name="android.intent.action.BOOT_COMPLETED" /> | |
<category android:name="android.intent.category.DEFAULT" /> | |
</intent-filter> | |
</receiver> | |
</application> | |
</manifest> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package xxx | |
import android.app.* | |
import android.app.usage.UsageEvents | |
import android.app.usage.UsageStatsManager | |
import android.content.Context | |
import android.content.Intent | |
import android.util.Log | |
import java.util.* | |
class AppMonitorService : IntentService(AppMonitorService::class.simpleName) { | |
companion object { | |
private val TAG = AppMonitorService::class.qualifiedName | |
} | |
private val notificationId = Random().nextInt() | |
private var running: Boolean = false | |
private var workerThread: Thread? = null | |
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { | |
running = false | |
workerThread?.interrupt() | |
super.onStartCommand(intent, flags, startId) | |
return Service.START_STICKY | |
} | |
override fun onHandleIntent(intent: Intent?) { | |
if (!isUsageStatsAllowed()) { | |
allowUsageStats() | |
} | |
startNotification() | |
monitorApp() | |
} | |
private fun monitorApp() { | |
workerThread = Thread.currentThread() | |
var endTime = System.currentTimeMillis() | |
running = true | |
do { | |
val beginTime = endTime | |
endTime = System.currentTimeMillis() | |
getForegroundApps(beginTime, endTime).lastOrNull()?.let { | |
Log.i(TAG, it) | |
} | |
sleepMonitor() | |
} while (running) | |
} | |
private fun sleepMonitor() { | |
try { | |
Thread.sleep(1000) | |
} | |
catch (e : InterruptedException) { | |
} | |
} | |
private fun startNotification() { | |
val activityIntent = Intent(this, MainActivity::class.java) | |
val pendingIntent = PendingIntent.getActivity(this, 0, activityIntent, 0) | |
val notification = Notification.Builder(this) | |
.setContentTitle(AppMonitorService::class.simpleName) | |
.setContentText("Service is running.") | |
.setContentIntent(pendingIntent) | |
.setSmallIcon(R.mipmap.ic_launcher) // required for setContentIntent | |
.build() | |
startForeground(notificationId, notification) | |
} | |
private fun getForegroundApps(beginTime: Long, endTime: Long): List<String> { | |
val foregroundApps = mutableListOf<String>() | |
val statsService = getSystemService(Context.USAGE_STATS_SERVICE) | |
if (statsService is UsageStatsManager) { | |
val events = statsService.queryEvents(beginTime, endTime) | |
val e = UsageEvents.Event() | |
while (events.getNextEvent(e)) { | |
if (e.eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) { | |
foregroundApps.add(e.packageName) | |
} | |
} | |
} else { | |
Log.e(TAG, "UsageStatsManager could not get.") | |
} | |
return foregroundApps | |
} | |
private fun isUsageStatsAllowed(): Boolean { | |
val opsService = getSystemService(Context.APP_OPS_SERVICE) | |
if (opsService is AppOpsManager) { | |
val mode = opsService.checkOpNoThrow( | |
AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), packageName) | |
return mode == AppOpsManager.MODE_ALLOWED | |
} else { | |
Log.e(TAG, "AppOpsManager could not get.") | |
return false | |
} | |
} | |
private fun allowUsageStats() { | |
startActivity(Intent("android.settings.USAGE_ACCESS_SETTINGS") | |
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package xxx | |
import android.content.Intent | |
import android.support.v7.app.AppCompatActivity | |
import android.os.Bundle | |
class MainActivity : AppCompatActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
setContentView(R.layout.activity_main) | |
startService(Intent(this, AppMonitorService::class.java)) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package xxx | |
import android.content.BroadcastReceiver | |
import android.content.Context | |
import android.content.Intent | |
class StartupReceiver : BroadcastReceiver() { | |
override fun onReceive(context: Context, intent: Intent) { | |
context.startService(Intent(context, AppMonitorService::class.java)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment