Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,12 @@ class SafeServiceConnection<T : Service>(
/**
* Binds the service if it is currently running.
*/
fun bindService() {
fun bindService(): Boolean {
if (!mIsBound && Utils.isServiceRunning(context, serviceClass)) {
try {
val bindIntent = Intent(context, serviceClass)
bindIntent.setAction(ServiceBinder.ACTION_BIND_TO_MINDFUL)
context.bindService(bindIntent, this, Context.BIND_WAIVE_PRIORITY)
return context.bindService(bindIntent, this, Context.BIND_WAIVE_PRIORITY)
} catch (e: Exception) {
SharedPrefsHelper.insertCrashLogToPrefs(context, e)
Log.e(
Expand All @@ -80,6 +80,7 @@ class SafeServiceConnection<T : Service>(
)
}
}
return mIsBound && Utils.isServiceRunning(context, serviceClass)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,7 @@ object AlarmTasksSchedulingHelper {
*/
fun scheduleBedtimeRoutineTasks(context: Context, jsonBedtimeSettings: String) {
val bedtimeSchedule = BedtimeSchedule.fromJson(jsonBedtimeSettings)
val extraMap = mapOf(
EXTRA_BEDTIME_SETTINGS_JSON to jsonBedtimeSettings
)
val extraMap = mapOf(EXTRA_BEDTIME_SETTINGS_JSON to jsonBedtimeSettings)

val nowInMs = System.currentTimeMillis()
var alertTimeMs = todToTodayCal(bedtimeSchedule.scheduleStartTime - 30).timeInMillis
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.mindful.android.services.wellbeing

import android.content.Context
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread

interface IWellbeingModule {

@MainThread
fun onCreate(context: Context)

@MainThread
fun onConnected(context: Context)

@WorkerThread
fun onAccessibilityEvent(
context: Context,
event: AccessibilityEvent,
eventNode: AccessibilityNodeInfo?,
rootNode: AccessibilityNodeInfo?,
)

@MainThread
fun onInterrupt(context: Context)

@MainThread
fun onDestroy(context: Context)

@WorkerThread
fun onMidnightReset(context: Context)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package com.mindful.android.services.wellbeing

import android.accessibilityservice.AccessibilityService
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.util.Log
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.Toast
import com.mindful.android.helpers.storage.SharedPrefsHelper
import com.mindful.android.services.accessibility.TrackingManager.Companion.ACTION_ACCESSIBILITY_ACTIVE
import com.mindful.android.services.accessibility.TrackingManager.Companion.ACTION_ACCESSIBILITY_INACTIVE
import com.mindful.android.services.accessibility.TrackingManager.Companion.ACTION_NEW_APP_LAUNCHED
import com.mindful.android.utils.ThreadUtils
import com.mindful.android.utils.Utils
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors

class WellbeingService : AccessibilityService() {
companion object {
private const val TAG = "Mindful.WellbeingService"
}

private val executorService: ExecutorService = Executors.newFixedThreadPool(4)
private val wellbeingReceiver: WellbeingReceiver = WellbeingReceiver()
private val wellbeingModules = listOf<IWellbeingModule>()

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
return super.onStartCommand(intent, flags, startId)
}

override fun onCreate() {
super.onCreate()
runCatching {
wellbeingReceiver.register(this)
wellbeingModules.forEach { it.onCreate(this) }
}.getOrElse { onError(it) }
}

override fun onServiceConnected() {
super.onServiceConnected()
runCatching {
wellbeingModules.forEach { it.onConnected(this) }
}.getOrElse { onError(it) }
}

override fun onAccessibilityEvent(event: AccessibilityEvent?) {
if (event == null || executorService.isShutdown) return

// Process event in background
runCatching {
executorService.submit {
// Clone event and nodes for processing
val eventClone =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) AccessibilityEvent(event)
else AccessibilityEvent.obtain(event)

val eventNodeClone = event.source?.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) AccessibilityNodeInfo(it)
else AccessibilityNodeInfo.obtain(it)
}

val rootNodeClone = rootInActiveWindow?.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) AccessibilityNodeInfo(it)
else AccessibilityNodeInfo.obtain(it)
}

wellbeingModules.forEach {
it.onAccessibilityEvent(
context = this,
event = eventClone,
eventNode = eventNodeClone,
rootNode = rootNodeClone,
)
}
}
}.getOrElse { onError(it) }
}

override fun onInterrupt() {
runCatching {
wellbeingModules.forEach { it.onInterrupt(this) }
}.getOrElse { onError(it) }
}


override fun onDestroy() {
super.onDestroy()
runCatching {
wellbeingReceiver.unRegister(this)
executorService.shutdown()
wellbeingModules.forEach { it.onDestroy(this) }
}.getOrElse { onError(it) }
}

private fun performAction(
globalAction: Int? = null,
toastMsg: String? = null,
) {
runCatching {
// Perform global action
globalAction?.let { performGlobalAction(it) }

// Show toast on main thread
toastMsg?.let {
ThreadUtils.runOnMainThread {
Toast.makeText(
this,
it,
Toast.LENGTH_LONG,
).show()
}
}
}.getOrElse { onError(it) }
}

private fun onError(throwable: Throwable) {
// TODO : Implement logging anc crashlytics
}


inner class WellbeingReceiver : BroadcastReceiver() {
private val TAG = "Mindful.WellbeingService.WellbeingReceiver"


fun register(context: Context) {
try {
Utils.safelyRegisterReceiver(
context,
this,
IntentFilter().apply {
addAction(ACTION_ACCESSIBILITY_ACTIVE)
addAction(ACTION_ACCESSIBILITY_INACTIVE)
addAction(ACTION_NEW_APP_LAUNCHED)
},
)
} catch (e: Exception) {
Log.e(TAG, "register: Failed to register receiver", e)
SharedPrefsHelper.insertCrashLogToPrefs(context, e)
}
}

fun unRegister(context: Context) {
try {
context.unregisterReceiver(this)
} catch (e: Exception) {
Log.e(TAG, "register: Failed to un-register receiver", e)
SharedPrefsHelper.insertCrashLogToPrefs(context, e)
}
}

override fun onReceive(context: Context?, intent: Intent?) {

// Handle broadcasted events
executorService.submit {
when (intent?.action) {

else -> return@submit
}
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ class AggregatedUsageWidgetProvider : AppWidgetProvider() {
-> {
val appWidgetManager = AppWidgetManager.getInstance(context)
val widgetComponent =
ComponentName(context, ScreenUsageWidgetProvider::class.java)
ComponentName(context, AggregatedUsageWidgetProvider::class.java)
val appWidgetIds = appWidgetManager.getAppWidgetIds(widgetComponent)
updateWidgetAsync(
context = context,
Expand Down