Skip to content
Merged
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
28 changes: 6 additions & 22 deletions app/src/main/java/org/obd/graphs/activity/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,10 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.PowerManager
import android.os.StrictMode
import android.os.StrictMode.ThreadPolicy
import android.os.StrictMode.VmPolicy
import android.provider.Settings
import android.util.Log
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
Expand All @@ -41,6 +36,7 @@ import org.obd.graphs.BuildConfig
import org.obd.graphs.ExceptionHandler
import org.obd.graphs.MAIN_ACTIVITY_EVENT_DESTROYED
import org.obd.graphs.MAIN_ACTIVITY_EVENT_PAUSE
import org.obd.graphs.Permissions
import org.obd.graphs.R
import org.obd.graphs.bl.datalogger.dataLogger
import org.obd.graphs.bl.drag.dragRacingMetricsProcessor
Expand Down Expand Up @@ -161,13 +157,14 @@ class MainActivity :
setupLeftNavigationPanel()
supportActionBar?.hide()
setupMetricsProcessors()
setupBatteryOptimization()
backupManager = BackupManager(this)
displayAppSignature(this)

navigateToLastVisitedScreen()
validatePermissions()
}


override fun onResume() {
super.onResume()
screen.setupWindowManager(this)
Expand Down Expand Up @@ -258,22 +255,9 @@ class MainActivity :
}
}

private fun setupBatteryOptimization() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val registered = (getSystemService(POWER_SERVICE) as PowerManager).isIgnoringBatteryOptimizations(packageName)
Log.i(
LOG_TAG,
"Checking permissions related to battery optimization. Ignoring Battery Optimization for package=$packageName, " +
"registered=$registered",
)
if (!registered) {
startActivity(
Intent().apply {
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
data = Uri.parse("package:$packageName")
},
)
}
private fun validatePermissions() {
if (Permissions.isAnyPermissionMissing(this)) {
Permissions.showPermissionOnboarding(this)
}
}
}
113 changes: 112 additions & 1 deletion common/src/main/java/org/obd/graphs/Permissions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@
package org.obd.graphs

import android.Manifest
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.location.LocationManager
import android.net.Uri
import android.os.Build
import android.os.PowerManager
import android.provider.Settings
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.location.LocationManagerCompat
Expand All @@ -33,7 +38,60 @@ private const val LOCATION_REQUEST_CODE = 1001
private const val BLUETOOTH_REQUEST_CODE = 1002
private const val NOTIFICATION_REQUEST_CODE = 1003

object Permissions {
@SuppressLint("ObsoleteSdkInt")
object Permissions {

/**
* Returns TRUE if any required permission is missing.
* Reuses your existing individual checks.
*/
fun isAnyPermissionMissing(context: Context): Boolean {
if (!hasLocationPermissions(context)) return true
if (!hasNotificationPermissions(context)) return true
if (!isBatteryOptimizationEnabled(context)) return true

val btPerms = getBluetoothPermissions()
return !EasyPermissions.hasPermissions(context, *btPerms)
}

fun showPermissionOnboarding(activity: Activity) {
val message =
"""
To provide full functionality, please allow the following in the next steps:
• Battery Optimization: To ensure data logging isn't interrupted in the background.
• Location: To track your trip via GPS.
• Bluetooth: To connect to your OBD adapter.
• Notifications: To keep the logger running in the background.
""".trimIndent()

androidx.appcompat.app.AlertDialog
.Builder(activity)
.setTitle("Setup Required")
.setMessage(message)
.setPositiveButton("Begin Setup") { _, _ ->
requestAll(activity)

if (!isBatteryOptimizationEnabled(activity)) {
requestBatteryOptimization(activity)
}
}.setNegativeButton("Later", null)
.show()
}



/**
* Checks if the app is already ignoring battery optimizations.
*/
@SuppressLint("ObsoleteSdkInt")
fun isBatteryOptimizationEnabled(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val powerManager = context.getSystemService(Context.POWER_SERVICE) as? PowerManager
return powerManager?.isIgnoringBatteryOptimizations(context.packageName) ?: true
}
return true
}

/**
* Returns TRUE if all required location permissions are granted.
* Also performs a diagnostic check to warn if the user has selected "Approximate" location.
Expand Down Expand Up @@ -165,4 +223,57 @@ object Permissions {
}
return perms.toTypedArray()
}

/**
* Aggregates and requests all required permissions for the application.
* Use this at app startup to minimize the number of pop-ups.
*/
private fun requestAll(activity: Activity) {
val perms = mutableListOf<String>()

perms.add(Manifest.permission.ACCESS_COARSE_LOCATION)
perms.add(Manifest.permission.ACCESS_FINE_LOCATION)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
perms.add(Manifest.permission.BLUETOOTH_SCAN)
perms.add(Manifest.permission.BLUETOOTH_CONNECT)
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
perms.add(Manifest.permission.POST_NOTIFICATIONS)
}

val missingPermissions =
perms.filter {
ContextCompat.checkSelfPermission(activity, it) != PackageManager.PERMISSION_GRANTED
}

if (missingPermissions.isNotEmpty()) {
Log.i(TAG, "Requesting missing permissions: $missingPermissions")

EasyPermissions.requestPermissions(
activity,
"This app requires Location, Bluetooth, and Notification permissions to function correctly.",
1000, // Use a generic ALL_PERMISSIONS_REQUEST_CODE
*missingPermissions.toTypedArray(),
)
} else {
Log.v(TAG, "All permissions already granted.")
}
}

/**
* Triggers the battery optimization intent.
* Note: This must be called from an Activity.
*/
private fun requestBatteryOptimization(activity: Activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
Log.i(TAG, "Requesting to ignore battery optimizations.")
val intent = Intent().apply {
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
data = Uri.parse("package:${activity.packageName}")
}
activity.startActivity(intent)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ internal class GpsMetricsEmitter : MetricsProcessor {
}

override fun onStopped() {
Log.i(TAG, "Stopping GPS updates")
stopGpsUpdates()
}

Expand Down Expand Up @@ -147,6 +146,9 @@ internal class GpsMetricsEmitter : MetricsProcessor {

private fun stopGpsUpdates() {
try {

Log.i(TAG, "Stopping GPS updates")

locationListener?.let { locationManager?.removeUpdates(it) }

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,11 @@ internal class DefaultProfileService :
}

if (pref.startsWith("profile_") || pref == getInstallationVersion()) {
Log.v(PROFILE_AUTO_SAVER_LOG_TAG, "Skipping: $pref")
if (Log.isLoggable(PROFILE_AUTO_SAVER_LOG_TAG,Log.VERBOSE)) {
Log.v(PROFILE_AUTO_SAVER_LOG_TAG, "Skipping: $pref")
} else {
//
}
} else {
val profileName = getCurrentProfile()
ss?.edit {
Expand Down
Loading