mirror of
https://codeberg.org/timelimit/timelimit-android.git
synced 2025-10-03 09:49:25 +02:00
Refactor InstanceIdForegroundAppHelper
This commit is contained in:
parent
8b0463c6d6
commit
72c3da3d0d
8 changed files with 208 additions and 95 deletions
|
@ -16,7 +16,6 @@
|
||||||
package io.timelimit.android.integration.platform.android.foregroundapp
|
package io.timelimit.android.integration.platform.android.foregroundapp
|
||||||
|
|
||||||
import android.annotation.TargetApi
|
import android.annotation.TargetApi
|
||||||
import android.app.usage.UsageEvents
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
|
@ -24,6 +23,8 @@ import androidx.core.util.size
|
||||||
import io.timelimit.android.coroutines.executeAndWait
|
import io.timelimit.android.coroutines.executeAndWait
|
||||||
import io.timelimit.android.integration.platform.ForegroundApp
|
import io.timelimit.android.integration.platform.ForegroundApp
|
||||||
import io.timelimit.android.integration.platform.RuntimePermissionStatus
|
import io.timelimit.android.integration.platform.RuntimePermissionStatus
|
||||||
|
import io.timelimit.android.integration.platform.android.foregroundapp.usagestats.DirectUsageStatsReader
|
||||||
|
import io.timelimit.android.integration.platform.android.foregroundapp.usagestats.UsageStatsConstants
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.Q)
|
@TargetApi(Build.VERSION_CODES.Q)
|
||||||
class InstanceIdForegroundAppHelper(context: Context): UsageStatsForegroundAppHelper(context) {
|
class InstanceIdForegroundAppHelper(context: Context): UsageStatsForegroundAppHelper(context) {
|
||||||
|
@ -35,16 +36,11 @@ class InstanceIdForegroundAppHelper(context: Context): UsageStatsForegroundAppHe
|
||||||
private var lastQueryTime = 0L
|
private var lastQueryTime = 0L
|
||||||
private var lastEventTimestamp = 0L
|
private var lastEventTimestamp = 0L
|
||||||
private val apps = SparseArray<ForegroundApp>()
|
private val apps = SparseArray<ForegroundApp>()
|
||||||
private val nativeEvent = UsageEvents.Event()
|
|
||||||
|
|
||||||
override suspend fun getForegroundApps(
|
override suspend fun getForegroundApps(
|
||||||
queryInterval: Long,
|
queryInterval: Long,
|
||||||
experimentalFlags: Long
|
experimentalFlags: Long
|
||||||
): Set<ForegroundApp> {
|
): Set<ForegroundApp> {
|
||||||
if (Build.VERSION.SDK_INT > 32) {
|
|
||||||
throw InstanceIdException.UntestedSystemVersionException()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getPermissionStatus() != RuntimePermissionStatus.Granted) {
|
if (getPermissionStatus() != RuntimePermissionStatus.Granted) {
|
||||||
throw SecurityException()
|
throw SecurityException()
|
||||||
}
|
}
|
||||||
|
@ -74,54 +70,27 @@ class InstanceIdForegroundAppHelper(context: Context): UsageStatsForegroundAppHe
|
||||||
val queryEndTime = now + TOLERANCE
|
val queryEndTime = now + TOLERANCE
|
||||||
|
|
||||||
usageStatsManager.queryEvents(queryStartTime, queryEndTime)?.let { nativeEvents ->
|
usageStatsManager.queryEvents(queryStartTime, queryEndTime)?.let { nativeEvents ->
|
||||||
val events = TlUsageEvents.fromUsageEvents(nativeEvents)
|
val events = DirectUsageStatsReader(nativeEvents)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var isFirstEvent = true
|
var isFirstEvent = true
|
||||||
|
|
||||||
while (true) {
|
while (events.loadNextEvent()) {
|
||||||
// loop condition with additional checks
|
|
||||||
val didReadEvent = kotlin.run {
|
|
||||||
val didReadNativeEvent = nativeEvents.getNextEvent(nativeEvent)
|
|
||||||
val didReadTlEvent = events.readNextItem()
|
|
||||||
|
|
||||||
if (didReadNativeEvent != didReadTlEvent) {
|
|
||||||
throw InstanceIdException.NotMatchingData(
|
|
||||||
if (didReadTlEvent) "events got next event but nativeEvents not"
|
|
||||||
else "nativeEvents got next event but events not"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
didReadTlEvent // == didReadNativeEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!didReadEvent) break
|
|
||||||
|
|
||||||
// check the consistency
|
// check the consistency
|
||||||
kotlin.run {
|
|
||||||
if (events.eventType != nativeEvent.eventType) {
|
|
||||||
throw InstanceIdException.NotMatchingData("got different eventTypes: ${events.eventType} vs ${nativeEvent.eventType}")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (events.timestamp != nativeEvent.timeStamp) {
|
|
||||||
throw InstanceIdException.NotMatchingData("got different timestamps: ${events.timestamp} vs ${nativeEvent.timeStamp}")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (events.timestamp < lastEventTimestamp && !isFirstEvent) {
|
if (events.timestamp < lastEventTimestamp && !isFirstEvent) {
|
||||||
throw InstanceIdException.EventsNotSortedByTimestamp()
|
throw InstanceIdException.EventsNotSortedByTimestamp()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// process the event
|
// process the event
|
||||||
if (events.eventType == TlUsageEvents.DEVICE_STARTUP) {
|
if (events.eventType == UsageStatsConstants.DEVICE_STARTUP) {
|
||||||
apps.clear()
|
apps.clear()
|
||||||
} else if (events.eventType == TlUsageEvents.MOVE_TO_FOREGROUND) {
|
} else if (events.eventType == UsageStatsConstants.MOVE_TO_FOREGROUND) {
|
||||||
val app = ForegroundApp(events.packageName, events.className)
|
val app = ForegroundApp(events.packageName, events.className)
|
||||||
|
|
||||||
apps.put(events.instanceId, app)
|
apps.put(events.instanceId, app)
|
||||||
} else if (
|
} else if (
|
||||||
events.eventType == TlUsageEvents.MOVE_TO_BACKGROUND ||
|
events.eventType == UsageStatsConstants.MOVE_TO_BACKGROUND ||
|
||||||
events.eventType == TlUsageEvents.ACTIVITY_STOPPED
|
events.eventType == UsageStatsConstants.ACTIVITY_STOPPED
|
||||||
) {
|
) {
|
||||||
apps.remove(events.instanceId)
|
apps.remove(events.instanceId)
|
||||||
}
|
}
|
||||||
|
@ -132,9 +101,6 @@ class InstanceIdForegroundAppHelper(context: Context): UsageStatsForegroundAppHe
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
events.free()
|
events.free()
|
||||||
|
|
||||||
// the nativeEvents have no free function; but they release their data when everything was read
|
|
||||||
while (nativeEvents.getNextEvent(nativeEvent)) {/* consume all values */}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,8 +119,6 @@ class InstanceIdForegroundAppHelper(context: Context): UsageStatsForegroundAppHe
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class InstanceIdException(message: String): RuntimeException(message) {
|
sealed class InstanceIdException(message: String): RuntimeException(message) {
|
||||||
class UntestedSystemVersionException: InstanceIdException("untested system version")
|
|
||||||
class NotMatchingData(detail: String): InstanceIdException("not matching data: $detail")
|
|
||||||
class EventsNotSortedByTimestamp: InstanceIdException("events not sorted by timestamp")
|
class EventsNotSortedByTimestamp: InstanceIdException("events not sorted by timestamp")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.integration.platform.android.foregroundapp.usagestats
|
||||||
|
|
||||||
|
import android.app.usage.UsageEvents
|
||||||
|
|
||||||
|
class DirectUsageStatsReader (private val events: UsageEvents): UsageStatsReader {
|
||||||
|
private val event = UsageEvents.Event()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val getInstanceId = try {
|
||||||
|
UsageEvents.Event::class.java.getMethod("getInstanceId")
|
||||||
|
} catch (ex: NoSuchMethodException) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val instanceIdSupported = getInstanceId != null
|
||||||
|
}
|
||||||
|
|
||||||
|
override val timestamp: Long get() = event.timeStamp
|
||||||
|
override val eventType: Int get() = event.eventType
|
||||||
|
override val instanceId: Int get() = if (getInstanceId != null) getInstanceId.invoke(event) as Int
|
||||||
|
else throw InstanceIdUnsupportedException()
|
||||||
|
override val packageName: String get() = event.packageName
|
||||||
|
override val className: String get() = event.className ?: "null"
|
||||||
|
|
||||||
|
override fun loadNextEvent() = events.getNextEvent(event)
|
||||||
|
|
||||||
|
override fun free() {
|
||||||
|
while (events.getNextEvent(event)) {
|
||||||
|
// just consume it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstanceIdUnsupportedException: RuntimeException("instance id unsupported")
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.integration.platform.android.foregroundapp.usagestats
|
||||||
|
|
||||||
|
import java.lang.RuntimeException
|
||||||
|
|
||||||
|
class MergedUsageStatsReader(private val primary: UsageStatsReader, private val confirmation: UsageStatsReader): UsageStatsReader {
|
||||||
|
override val timestamp = primary.timestamp
|
||||||
|
override val eventType = primary.eventType
|
||||||
|
override val instanceId = primary.instanceId
|
||||||
|
override val packageName = primary.packageName
|
||||||
|
override val className = primary.className
|
||||||
|
|
||||||
|
override fun loadNextEvent(): Boolean {
|
||||||
|
val didReadEvent = kotlin.run {
|
||||||
|
val didReadPrimaryEvent = primary.loadNextEvent()
|
||||||
|
val didReadConfirmationEvent = confirmation.loadNextEvent()
|
||||||
|
|
||||||
|
if (didReadConfirmationEvent != didReadPrimaryEvent) {
|
||||||
|
throw NotMatchingDataException(
|
||||||
|
if (didReadPrimaryEvent) "primary got next event but confirmation not"
|
||||||
|
else "confirmation got next event but primary not"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
didReadPrimaryEvent // == didReadNativeEvent
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!didReadEvent) return false
|
||||||
|
|
||||||
|
// check the consistency
|
||||||
|
if (primary.eventType != confirmation.eventType) {
|
||||||
|
throw NotMatchingDataException("got different eventTypes: ${primary.eventType} vs ${confirmation.eventType}")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primary.timestamp != confirmation.timestamp) {
|
||||||
|
throw NotMatchingDataException("got different timestamps: ${primary.timestamp} vs ${confirmation.timestamp}")
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun free() {
|
||||||
|
try {
|
||||||
|
primary.free()
|
||||||
|
} finally {
|
||||||
|
confirmation.free()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NotMatchingDataException(detail: String): RuntimeException("not matching data: $detail")
|
||||||
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package io.timelimit.android.integration.platform.android.foregroundapp
|
package io.timelimit.android.integration.platform.android.foregroundapp.usagestats
|
||||||
|
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
|
@ -13,52 +13,33 @@
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package io.timelimit.android.integration.platform.android.foregroundapp
|
package io.timelimit.android.integration.platform.android.foregroundapp.usagestats
|
||||||
|
|
||||||
import android.app.usage.UsageEvents
|
import android.app.usage.UsageEvents
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
|
import android.os.Build
|
||||||
import android.os.Parcel
|
import android.os.Parcel
|
||||||
|
import java.lang.RuntimeException
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is/should be a Parcel compatible clone of the UsageEvents
|
* This class is/should be a Parcel compatible clone of the UsageEvents
|
||||||
* system class. This allows reading fields which the public API does not provide.
|
* system class. This allows reading fields which the public API does not provide.
|
||||||
*/
|
*/
|
||||||
class TlUsageEvents (private val content: Parcel) {
|
class ParcelUsageStatsReader (private val content: Parcel): UsageStatsReader {
|
||||||
companion object {
|
companion object {
|
||||||
const val MOVE_TO_FOREGROUND = 1
|
private const val CONFIGURATION_CHANGE = 5
|
||||||
const val MOVE_TO_BACKGROUND = 2
|
private const val SHORTCUT_INVOCATION = 8
|
||||||
// const val END_OF_DAY = 3
|
private const val CHOOSER_ACTION = 9
|
||||||
// const val CONTINUE_PREVIOUS_DAY = 4
|
private const val STANDBY_BUCKET_CHANGED = 11
|
||||||
const val CONFIGURATION_CHANGE = 5
|
private const val NOTIFICATION_INTERRUPTION = 12
|
||||||
// const val SYSTEM_INTERACTION = 6
|
private const val LOCUS_ID_SET = 30
|
||||||
// const val USER_INTERACTION = 7
|
private const val DUMMY_STRING = "null"
|
||||||
const val SHORTCUT_INVOCATION = 8
|
|
||||||
const val CHOOSER_ACTION = 9
|
|
||||||
// const val NOTIFICATION_SEEN = 10
|
|
||||||
const val STANDBY_BUCKET_CHANGED = 11
|
|
||||||
const val NOTIFICATION_INTERRUPTION = 12
|
|
||||||
// const val SLICE_PINNED_PRIV = 13
|
|
||||||
// const val SLICE_PINNED = 14
|
|
||||||
// const val SCREEN_INTERACTIVE = 15
|
|
||||||
// const val SCREEN_NON_INTERACTIVE = 16
|
|
||||||
// const val KEYGUARD_SHOWN = 17
|
|
||||||
// const val KEYGUARD_HIDDEN = 18
|
|
||||||
// const val FOREGROUND_SERVICE_START = 19
|
|
||||||
// const val FOREGROUND_SERVICE_STOP = 20
|
|
||||||
// const val CONTINUING_FOREGROUND_SERVICE = 21
|
|
||||||
// const val ROLLOVER_FOREGROUND_SERVICE = 22
|
|
||||||
const val ACTIVITY_STOPPED = 23
|
|
||||||
// const val ACTIVITY_DESTROYED = 24
|
|
||||||
// const val FLUSH_TO_DISK = 25
|
|
||||||
// const val DEVICE_SHUTDOWN = 26
|
|
||||||
const val DEVICE_STARTUP = 27
|
|
||||||
// const val USER_UNLOCKED = 28
|
|
||||||
// const val USER_STOPPED = 29
|
|
||||||
const val LOCUS_ID_SET = 30
|
|
||||||
// const val APP_COMPONENT_USED = 31
|
|
||||||
const val DUMMY_STRING = "null"
|
|
||||||
|
|
||||||
fun getParcel(input: UsageEvents): Parcel {
|
fun getParcel(input: UsageEvents): Parcel {
|
||||||
|
if (Build.VERSION.SDK_INT > 32) {
|
||||||
|
throw UntestedSystemVersionException()
|
||||||
|
}
|
||||||
|
|
||||||
val outerParcel = Parcel.obtain()
|
val outerParcel = Parcel.obtain()
|
||||||
|
|
||||||
val blob = try {
|
val blob = try {
|
||||||
|
@ -84,7 +65,9 @@ class TlUsageEvents (private val content: Parcel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromUsageEvents(input: UsageEvents): TlUsageEvents = TlUsageEvents(getParcel(input))
|
fun fromUsageEvents(input: UsageEvents): ParcelUsageStatsReader = ParcelUsageStatsReader(
|
||||||
|
getParcel(input)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var free = false
|
private var free = false
|
||||||
|
@ -110,13 +93,13 @@ class TlUsageEvents (private val content: Parcel) {
|
||||||
private var outputPackageName = DUMMY_STRING
|
private var outputPackageName = DUMMY_STRING
|
||||||
private var outputClassName = DUMMY_STRING
|
private var outputClassName = DUMMY_STRING
|
||||||
|
|
||||||
val timestamp get() = outputTimestamp
|
override val timestamp get() = outputTimestamp
|
||||||
val eventType get() = outputEventType
|
override val eventType get() = outputEventType
|
||||||
val instanceId get() = outputInstanceId
|
override val instanceId get() = outputInstanceId
|
||||||
val packageName get() = outputPackageName
|
override val packageName get() = outputPackageName
|
||||||
val className get() = outputClassName
|
override val className get() = outputClassName
|
||||||
|
|
||||||
fun readNextItem(): Boolean {
|
override fun loadNextEvent(): Boolean {
|
||||||
if (free) return false
|
if (free) return false
|
||||||
if (strings == null) throw IllegalStateException()
|
if (strings == null) throw IllegalStateException()
|
||||||
|
|
||||||
|
@ -163,11 +146,13 @@ class TlUsageEvents (private val content: Parcel) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun free() {
|
override fun free() {
|
||||||
if (!free) {
|
if (!free) {
|
||||||
content.recycle()
|
content.recycle()
|
||||||
|
|
||||||
free = true
|
free = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UntestedSystemVersionException: RuntimeException("untested system version for the Parcel implementation")
|
||||||
}
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.integration.platform.android.foregroundapp.usagestats
|
||||||
|
|
||||||
|
object UsageStatsConstants {
|
||||||
|
const val MOVE_TO_FOREGROUND = 1
|
||||||
|
const val MOVE_TO_BACKGROUND = 2
|
||||||
|
const val ACTIVITY_STOPPED = 23
|
||||||
|
const val DEVICE_STARTUP = 27
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
/*
|
||||||
|
* TimeLimit Copyright <C> 2019 - 2022 Jonas Lochmann
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation version 3 of the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package io.timelimit.android.integration.platform.android.foregroundapp.usagestats
|
||||||
|
|
||||||
|
interface UsageStatsReader {
|
||||||
|
val timestamp: Long
|
||||||
|
val eventType: Int
|
||||||
|
val instanceId: Int
|
||||||
|
val packageName: String
|
||||||
|
val className: String
|
||||||
|
|
||||||
|
fun loadNextEvent(): Boolean
|
||||||
|
fun free()
|
||||||
|
}
|
|
@ -38,7 +38,7 @@ import io.timelimit.android.R
|
||||||
import io.timelimit.android.async.Threads
|
import io.timelimit.android.async.Threads
|
||||||
import io.timelimit.android.databinding.DiagnoseForegroundAppFragmentBinding
|
import io.timelimit.android.databinding.DiagnoseForegroundAppFragmentBinding
|
||||||
import io.timelimit.android.integration.platform.android.foregroundapp.InstanceIdForegroundAppHelper
|
import io.timelimit.android.integration.platform.android.foregroundapp.InstanceIdForegroundAppHelper
|
||||||
import io.timelimit.android.integration.platform.android.foregroundapp.TlUsageEvents
|
import io.timelimit.android.integration.platform.android.foregroundapp.usagestats.ParcelUsageStatsReader
|
||||||
import io.timelimit.android.livedata.liveDataFromNonNullValue
|
import io.timelimit.android.livedata.liveDataFromNonNullValue
|
||||||
import io.timelimit.android.livedata.liveDataFromNullableValue
|
import io.timelimit.android.livedata.liveDataFromNullableValue
|
||||||
import io.timelimit.android.livedata.map
|
import io.timelimit.android.livedata.map
|
||||||
|
@ -182,7 +182,7 @@ class DiagnoseForegroundAppFragment : Fragment(), FragmentWithCustomTitle {
|
||||||
val currentData = service.queryEvents(now - InstanceIdForegroundAppHelper.START_QUERY_INTERVAL, now)
|
val currentData = service.queryEvents(now - InstanceIdForegroundAppHelper.START_QUERY_INTERVAL, now)
|
||||||
|
|
||||||
val bytes = try {
|
val bytes = try {
|
||||||
val parcel = TlUsageEvents.getParcel(currentData)
|
val parcel = ParcelUsageStatsReader.getParcel(currentData)
|
||||||
try { parcel.marshall() } finally { parcel.recycle() }
|
try { parcel.marshall() } finally { parcel.recycle() }
|
||||||
} finally {
|
} finally {
|
||||||
val event = UsageEvents.Event()
|
val event = UsageEvents.Event()
|
||||||
|
@ -225,9 +225,9 @@ class DiagnoseForegroundAppFragment : Fragment(), FragmentWithCustomTitle {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var nativeData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
var nativeData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
val parcel = TlUsageEvents.getParcel(currentData)
|
val parcel = ParcelUsageStatsReader.getParcel(currentData)
|
||||||
val bytes = parcel.marshall()
|
val bytes = parcel.marshall()
|
||||||
val reader = TlUsageEvents(parcel)
|
val reader = ParcelUsageStatsReader(parcel)
|
||||||
|
|
||||||
NativeEventReader(reader, bytes, parcel)
|
NativeEventReader(reader, bytes, parcel)
|
||||||
} else null
|
} else null
|
||||||
|
@ -266,7 +266,7 @@ class DiagnoseForegroundAppFragment : Fragment(), FragmentWithCustomTitle {
|
||||||
val positionBefore = native.parcel.dataPosition()
|
val positionBefore = native.parcel.dataPosition()
|
||||||
|
|
||||||
val result: ReadNextItemResult = try {
|
val result: ReadNextItemResult = try {
|
||||||
if (native.events.readNextItem()) ReadNextItemResult.NextItem
|
if (native.events.loadNextEvent()) ReadNextItemResult.NextItem
|
||||||
else ReadNextItemResult.EndOfData
|
else ReadNextItemResult.EndOfData
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
ReadNextItemResult.Error(ex)
|
ReadNextItemResult.Error(ex)
|
||||||
|
@ -349,7 +349,7 @@ class DiagnoseForegroundAppFragment : Fragment(), FragmentWithCustomTitle {
|
||||||
} else super.onActivityResult(requestCode, resultCode, data)
|
} else super.onActivityResult(requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class NativeEventReader(val events: TlUsageEvents, val bytes: ByteArray, val parcel: Parcel /* owned by TlUsageEvents */) {
|
internal class NativeEventReader(val events: ParcelUsageStatsReader, val bytes: ByteArray, val parcel: Parcel /* owned by TlUsageEvents */) {
|
||||||
fun free() { events.free() }
|
fun free() { events.free() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue