Skip to content

Commit

Permalink
Get the active app using events, not recent apps
Browse files Browse the repository at this point in the history
This approach is more accurate (doesn't misreport when the notification
shade is shown) and more efficient (events are already ordered so we
don't need to sort them).
  • Loading branch information
smichel17 committed Sep 8, 2017
1 parent 5529cb6 commit eddb583
Show file tree
Hide file tree
Showing 8 changed files with 153 additions and 168 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,15 @@ import com.jmstudios.redmoon.filter.overlay.OverlayFilter
import com.jmstudios.redmoon.model.Profile
import com.jmstudios.redmoon.util.*

import java.util.concurrent.Executors

import org.greenrobot.eventbus.Subscribe

class FilterService : Service() {

private lateinit var mFilter: Filter
private val mAnimator: ValueAnimator
private val mExecutor = Executors.newSingleThreadScheduledExecutor()

private var mProfile = activeProfile.off
set(value) {
Expand All @@ -61,7 +64,7 @@ class FilterService : Service() {
override fun onCreate() {
super.onCreate()
Log.i("onCreate")
mFilter = OverlayFilter(this)
mFilter = OverlayFilter(this, mExecutor)
}

override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Expand All @@ -87,6 +90,7 @@ class FilterService : Service() {
filterIsOn = false
mFilter.stop()
}
mExecutor.shutdownNow()
super.onDestroy()
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2016 Marien Raat <marienraat@riseup.net>
* Copyright (c) 2017 Stephen Michel <s@smichel.me>
* SPDX-License-Identifier: GPL-3.0+
*/
package com.jmstudios.redmoon.filter.overlay

import android.annotation.TargetApi
import android.app.ActivityManager
import android.app.usage.UsageEvents
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.ContextWrapper

import com.jmstudios.redmoon.util.*

class CurrentAppChecker(private val context: Context) {
val isWorking: Boolean = currentApp != ""

val currentApp: String
// http://stackoverflow.com/q/33581311
get() = try {
if (atLeastAPI(21)) {
currentAppFromUsageStats
} else {
currentAppFromActivityManager
}
} catch (e: Exception) {
""
}

private val currentAppFromUsageStats: String
@TargetApi(21) get() {
// Although the UsageStatsManager was added in API 21, the
// constant to specify it wasn't added until API 22.
// So we use the value of that constant on API 21.
val uss = if (belowAPI(22)) "usagestats" else @TargetApi(22) {
Context.USAGE_STATS_SERVICE
}
val usm = context.getSystemService(uss) as UsageStatsManager
val time = System.currentTimeMillis()
// Only look at events in the past 10 seconds
val eventList = usm.queryEvents(time - 1000 * 10, time)
val event = UsageEvents.Event()

tailrec fun findLastApp(app: String): String {
return if (eventList.hasNextEvent()) {
eventList.getNextEvent(event)
if (event.className == null) {
findLastApp(app)
} else {
findLastApp(event.packageName)
}
} else app
}

lastApp = findLastApp(lastApp)
return lastApp
}

private val currentAppFromActivityManager: String
@Suppress("DEPRECATION") get() {
val bc = ContextWrapper(context).baseContext
val am = bc.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
return am.getRunningTasks(1)[0].topActivity.packageName
}

companion object: Logger() {
private var lastApp: String = ""
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,39 @@ import android.content.IntentFilter
import android.os.PowerManager

import com.jmstudios.redmoon.model.Config
import com.jmstudios.redmoon.filter.Command
import com.jmstudios.redmoon.filter.ScreenStateReceiver
import com.jmstudios.redmoon.util.*

import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit

import org.greenrobot.eventbus.Subscribe

class CurrentAppMonitor(private val mContext: Context) : ScreenStateReceiver.ScreenStateListener {
companion object : Logger()
class CurrentAppMonitor(
private val mContext: Context,
private val mExecutor: ScheduledExecutorService)
: ScreenStateReceiver.ScreenStateListener {

private val screenStateReceiver = ScreenStateReceiver(this)
private var mCamThread: CurrentAppMonitoringThread? = null
private var mAppChecker = CurrentAppChecker(mContext)

private var mFuture: ScheduledFuture<*>? = null

private val handleCurrentApp = Runnable {
val currentApp = mAppChecker.currentApp
Log.i("Current app is: $currentApp")
when(currentApp) {
"com.android.packageinstaller",
"eu.chainfire.supersu",
"com.koushikdutta.superuser",
"me.phh.superuser",
"com.owncloud.android",
"com.google.android.packageinstaller" -> Command.SUSPEND.send()
else -> Command.RESUME.send()
}
}

private val powerManager: PowerManager
get() = appContext.getSystemService(Context.POWER_SERVICE) as PowerManager
Expand All @@ -29,20 +53,16 @@ class CurrentAppMonitor(private val mContext: Context) : ScreenStateReceiver.Scr
if (atLeastAPI(20)) isInteractive else @Suppress("DEPRECATION") isScreenOn
}

private var isMonitoring = false
private var isMonitoring: Boolean = false

override fun onScreenTurnedOn() {
Log.i("Screen turn on received")
startCamThread()
startMonitoring()
}

override fun onScreenTurnedOff() {
Log.i("Screen turn off received")
stopCamThread()
}

@Subscribe fun onSecureSuspendChanged(event: secureSuspendChanged) {
if (Config.secureSuspend) start() else stop()
stopMonitoring()
}

fun start() = when {
Expand All @@ -55,33 +75,38 @@ class CurrentAppMonitor(private val mContext: Context) : ScreenStateReceiver.Scr
addAction(Intent.ACTION_SCREEN_ON )
}
mContext.registerReceiver(screenStateReceiver, filter)
EventBus.register(this)
if (screenOn) startMonitoring()
isMonitoring = true
startCamThread()
}
}

fun stop() = if (!isMonitoring) {
Log.i("Monitoring is already stopped")
} else {
Log.i("Stopping app monitoring")
stopCamThread()
stopMonitoring()
try {
mContext.unregisterReceiver(screenStateReceiver)
EventBus.unregister(this)
} catch (e: IllegalArgumentException) {
// Catch errors when receiver is unregistered more than once.
// It is not a problem, so we just ignore it.
}
isMonitoring = false
}

private fun startCamThread() {
if (mCamThread == null && screenOn) {
mCamThread = CurrentAppMonitoringThread(mContext).apply { start() }
}
private fun startMonitoring() {
mFuture = mExecutor.scheduleWithFixedDelay(handleCurrentApp, 0, 1, TimeUnit.SECONDS)
}

private fun stopCamThread() = mCamThread?.run{
if (!isInterrupted) { interrupt() }
mCamThread = null
private fun stopMonitoring() {
mFuture?.cancel(true)
}

@Subscribe fun onSecureSuspendChanged(event: secureSuspendChanged) {
if (Config.secureSuspend) start() else stop()
}

companion object : Logger()
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,26 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter

class OrientationChangeReceiver(private val mListener: OnOrientationChangeListener) : BroadcastReceiver() {
class OrientationChangeReceiver(
private val mContext: Context,
private val mListener: OnOrientationChangeListener)
: BroadcastReceiver() {

override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_CONFIGURATION_CHANGED) {
mListener.onOrientationChanged()
}
}

fun register(context: Context) {
context.registerReceiver(this, IntentFilter().apply {
private val filter by lazy {
IntentFilter().apply {
addAction(Intent.ACTION_CONFIGURATION_CHANGED)
})
}
}

fun unregister(context: Context) {
context.unregisterReceiver(this)
}
fun register(): Intent? = mContext.registerReceiver(this, filter)

fun unregister() = mContext.unregisterReceiver(this)

interface OnOrientationChangeListener {
fun onOrientationChanged()
Expand Down

0 comments on commit eddb583

Please sign in to comment.