Сравнить коммиты

...
8 Коммитов
Автор SHA1 Сообщение Дата
Anton Tananaev 5f059645a1 Update project 2022-05-29 10:50:11 -07:00
Anton Tananaev 8b985526dd Update version number 2022-01-30 10:18:36 -08:00
Anton Tananaev 5e8ea134e0 Attempt at fixing crashes 2022-01-30 10:18:09 -08:00
Anton Tananaev ec85edd56f Update version number 2021-11-04 19:21:19 -07:00
Anton Tananaev c5ab10919e Update dependencies 2021-11-04 19:20:08 -07:00
Anton Tananaev 3e8ba9a603 Fix Android 11 location permissions 2021-11-04 19:18:09 -07:00
Anton Tananaev 826d638ea3 Update version number 2021-10-11 23:26:51 -07:00
Anton Tananaev 1b687bd075 Handle security exception 2021-10-11 23:26:28 -07:00
8 изменённых файлов: 84 добавлений и 46 удалений
+15 -18
Просмотреть файл
@@ -2,29 +2,23 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
android { android {
compileSdkVersion 31 compileSdkVersion 32
defaultConfig { defaultConfig {
applicationId 'org.traccar.client' applicationId 'org.traccar.client'
buildConfigField 'boolean', 'HIDDEN_APP', 'false' buildConfigField 'boolean', 'HIDDEN_APP', 'false'
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 31 targetSdkVersion 32
versionCode 74 versionCode 78
versionName '6.13' versionName '6.17'
multiDexEnabled true multiDexEnabled true
} }
namespace 'org.traccar.client'
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions { packagingOptions {
exclude 'META-INF/DEPENDENCIES' resources {
excludes += ['META-INF/DEPENDENCIES']
} }
lintOptions {
checkReleaseBuilds false
} }
flavorDimensions 'default' flavorDimensions 'default'
@@ -44,14 +38,17 @@ android {
includeAndroidResources = true includeAndroidResources = true
} }
} }
lint {
checkReleaseBuilds false
}
} }
dependencies { dependencies {
implementation 'com.google.android.material:material:1.5.0-alpha04' implementation 'com.google.android.material:material:1.6.0'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.2.0'
implementation 'androidx.test:core-ktx:1.4.0' implementation 'androidx.test:core-ktx:1.4.0'
implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.core:core-ktx:1.7.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.github.judemanutd:autostarter:1.1.0' implementation 'com.github.judemanutd:autostarter:1.1.0'
implementation('dev.doubledot.doki:library:0.0.1@aar') { implementation('dev.doubledot.doki:library:0.0.1@aar') {
@@ -63,8 +60,8 @@ dependencies {
googleImplementation 'com.google.firebase:firebase-core' googleImplementation 'com.google.firebase:firebase-core'
googleImplementation 'com.google.firebase:firebase-analytics' googleImplementation 'com.google.firebase:firebase-analytics'
googleImplementation 'com.google.firebase:firebase-crashlytics' googleImplementation 'com.google.firebase:firebase-crashlytics'
googleImplementation 'com.google.android.gms:play-services-location:18.0.0' googleImplementation 'com.google.android.gms:play-services-location:19.0.1'
googleImplementation 'com.google.android.play:core:1.10.2' googleImplementation 'com.google.android.play:core:1.10.3'
} }
if (getGradle().getStartParameter().getTaskRequests().toString().contains('Google')) { if (getGradle().getStartParameter().getTaskRequests().toString().contains('Google')) {
+3 -3
Просмотреть файл
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest <manifest
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools">
package="org.traccar.client">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
@@ -26,7 +26,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:name=".MainApplication" android:name=".MainApplication"
tools:ignore="GoogleAppIndexingWarning"> tools:ignore="GoogleAppIndexingWarning,UnusedAttribute">
<meta-data <meta-data
android:name="google_analytics_adid_collection_enabled" android:name="google_analytics_adid_collection_enabled"
+9 -2
Просмотреть файл
@@ -66,7 +66,7 @@ class BatteryOptimizationHelper {
} }
} }
fun requestException(context: Context) { fun requestException(context: Context): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
if (!sharedPreferences.getBoolean(KEY_EXCEPTION_REQUESTED, false)) { if (!sharedPreferences.getBoolean(KEY_EXCEPTION_REQUESTED, false)) {
@@ -80,13 +80,20 @@ class BatteryOptimizationHelper {
requestVendorException(context) requestVendorException(context)
} }
} }
return true
} }
} else if (!sharedPreferences.getBoolean(KEY_AUTOSTART_REQUESTED, false)) { } else if (!sharedPreferences.getBoolean(KEY_AUTOSTART_REQUESTED, false)) {
sharedPreferences.edit().putBoolean(KEY_AUTOSTART_REQUESTED, true).apply() sharedPreferences.edit().putBoolean(KEY_AUTOSTART_REQUESTED, true).apply()
AutoStartPermissionHelper.getInstance().getAutoStartPermission(context) try {
if (AutoStartPermissionHelper.getInstance().getAutoStartPermission(context)) {
return true
}
} catch (e: SecurityException) {
} }
} }
} }
return false
}
companion object { companion object {
private const val KEY_EXCEPTION_REQUESTED = "exceptionRequested" private const val KEY_EXCEPTION_REQUESTED = "exceptionRequested"
+43 -10
Просмотреть файл
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012 - 2021 Anton Tananaev (anton@traccar.org) * Copyright 2012 - 2022 Anton Tananaev (anton@traccar.org)
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -54,6 +54,7 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
private lateinit var sharedPreferences: SharedPreferences private lateinit var sharedPreferences: SharedPreferences
private lateinit var alarmManager: AlarmManager private lateinit var alarmManager: AlarmManager
private lateinit var alarmIntent: PendingIntent private lateinit var alarmIntent: PendingIntent
private var requestingPermissions: Boolean = false
@SuppressLint("UnspecifiedImmutableFlag") @SuppressLint("UnspecifiedImmutableFlag")
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
@@ -61,7 +62,7 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
removeLauncherIcon() removeLauncherIcon()
} }
setHasOptionsMenu(true) setHasOptionsMenu(true)
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context) sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
setPreferencesFromResource(R.xml.preferences, rootKey) setPreferencesFromResource(R.xml.preferences, rootKey)
initPreferences() initPreferences()
@@ -95,9 +96,9 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
originalIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND) originalIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
} else { } else {
0 PendingIntent.FLAG_UPDATE_CURRENT
} }
alarmIntent = PendingIntent.getBroadcast(activity, 0, originalIntent, flags) alarmIntent = PendingIntent.getBroadcast(activity, 0, originalIntent, flags)
@@ -125,6 +126,7 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
} }
} }
@Suppress("DEPRECATION")
override fun onDisplayPreferenceDialog(preference: Preference) { override fun onDisplayPreferenceDialog(preference: Preference) {
if (listOf(KEY_INTERVAL, KEY_DISTANCE, KEY_ANGLE).contains(preference.key)) { if (listOf(KEY_INTERVAL, KEY_DISTANCE, KEY_ANGLE).contains(preference.key)) {
val f: EditTextPreferenceDialogFragmentCompat = val f: EditTextPreferenceDialogFragmentCompat =
@@ -154,6 +156,13 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
} }
} }
override fun onStart() {
super.onStart()
if (requestingPermissions) {
requestingPermissions = BatteryOptimizationHelper().requestException(requireContext())
}
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
sharedPreferences.registerOnSharedPreferenceChangeListener(this) sharedPreferences.registerOnSharedPreferenceChangeListener(this)
@@ -205,7 +214,7 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
} }
private fun initPreferences() { private fun initPreferences() {
PreferenceManager.setDefaultValues(activity, R.xml.preferences, false) PreferenceManager.setDefaultValues(requireActivity(), R.xml.preferences, false)
if (!sharedPreferences.contains(KEY_DEVICE)) { if (!sharedPreferences.contains(KEY_DEVICE)) {
val id = (Random().nextInt(900000) + 100000).toString() val id = (Random().nextInt(900000) + 100000).toString()
sharedPreferences.edit().putString(KEY_DEVICE, id).apply() sharedPreferences.edit().putString(KEY_DEVICE, id).apply()
@@ -214,6 +223,19 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
findPreference<Preference>(KEY_DEVICE)?.summary = sharedPreferences.getString(KEY_DEVICE, null) findPreference<Preference>(KEY_DEVICE)?.summary = sharedPreferences.getString(KEY_DEVICE, null)
} }
private fun showBackgroundLocationDialog(context: Context, onSuccess: () -> Unit) {
val builder = AlertDialog.Builder(context)
val option = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
context.packageManager.backgroundPermissionOptionLabel
} else {
context.getString(R.string.request_background_option)
}
builder.setMessage(context.getString(R.string.request_background, option))
builder.setPositiveButton(android.R.string.ok) { _, _ -> onSuccess() }
builder.setNegativeButton(android.R.string.cancel, null)
builder.show()
}
private fun startTrackingService(checkPermission: Boolean, initialPermission: Boolean) { private fun startTrackingService(checkPermission: Boolean, initialPermission: Boolean) {
var permission = initialPermission var permission = initialPermission
if (checkPermission) { if (checkPermission) {
@@ -224,10 +246,7 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
permission = requiredPermissions.isEmpty() permission = requiredPermissions.isEmpty()
if (!permission) { if (!permission) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions( requestPermissions(requiredPermissions.toTypedArray(), PERMISSIONS_REQUEST_LOCATION)
requiredPermissions.toTypedArray(),
PERMISSIONS_REQUEST_LOCATION
)
} }
return return
} }
@@ -235,11 +254,22 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
if (permission) { if (permission) {
setPreferencesEnabled(false) setPreferencesEnabled(false)
ContextCompat.startForegroundService(requireContext(), Intent(activity, TrackingService::class.java)) ContextCompat.startForegroundService(requireContext(), Intent(activity, TrackingService::class.java))
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
alarmManager.setInexactRepeating( alarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP, AlarmManager.ELAPSED_REALTIME_WAKEUP,
ALARM_MANAGER_INTERVAL.toLong(), ALARM_MANAGER_INTERVAL.toLong(), alarmIntent ALARM_MANAGER_INTERVAL.toLong(), ALARM_MANAGER_INTERVAL.toLong(), alarmIntent
) )
BatteryOptimizationHelper().requestException(requireContext()) }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
&& ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestingPermissions = true
showBackgroundLocationDialog(requireContext()) {
requestPermissions(arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), PERMISSIONS_REQUEST_BACKGROUND_LOCATION)
}
} else {
requestingPermissions = BatteryOptimizationHelper().requestException(requireContext())
}
} else { } else {
sharedPreferences.edit().putBoolean(KEY_STATUS, false).apply() sharedPreferences.edit().putBoolean(KEY_STATUS, false).apply()
val preference = findPreference<TwoStatePreference>(KEY_STATUS) val preference = findPreference<TwoStatePreference>(KEY_STATUS)
@@ -248,7 +278,9 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
} }
private fun stopTrackingService() { private fun stopTrackingService() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
alarmManager.cancel(alarmIntent) alarmManager.cancel(alarmIntent)
}
requireActivity().stopService(Intent(activity, TrackingService::class.java)) requireActivity().stopService(Intent(activity, TrackingService::class.java))
setPreferencesEnabled(true) setPreferencesEnabled(true)
} }
@@ -292,6 +324,7 @@ class MainFragment : PreferenceFragmentCompat(), OnSharedPreferenceChangeListene
const val KEY_BUFFER = "buffer" const val KEY_BUFFER = "buffer"
const val KEY_WAKELOCK = "wakelock" const val KEY_WAKELOCK = "wakelock"
private const val PERMISSIONS_REQUEST_LOCATION = 2 private const val PERMISSIONS_REQUEST_LOCATION = 2
private const val PERMISSIONS_REQUEST_BACKGROUND_LOCATION = 3
} }
} }
+2 -3
Просмотреть файл
@@ -57,11 +57,10 @@ class TrackingService : Service() {
@SuppressLint("WakelockTimeout") @SuppressLint("WakelockTimeout")
override fun onCreate() { override fun onCreate() {
startForeground(NOTIFICATION_ID, createNotification(this))
Log.i(TAG, "service create") Log.i(TAG, "service create")
sendBroadcast(Intent(ACTION_STARTED)) sendBroadcast(Intent(ACTION_STARTED))
StatusActivity.addMessage(getString(R.string.status_service_create)) StatusActivity.addMessage(getString(R.string.status_service_create))
startForeground(NOTIFICATION_ID, createNotification(this))
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(MainFragment.KEY_WAKELOCK, true)) { if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean(MainFragment.KEY_WAKELOCK, true)) {
@@ -89,10 +88,10 @@ class TrackingService : Service() {
} }
override fun onDestroy() { override fun onDestroy() {
stopForeground(true)
Log.i(TAG, "service destroy") Log.i(TAG, "service destroy")
sendBroadcast(Intent(ACTION_STOPPED)) sendBroadcast(Intent(ACTION_STOPPED))
StatusActivity.addMessage(getString(R.string.status_service_destroy)) StatusActivity.addMessage(getString(R.string.status_service_destroy))
stopForeground(true)
if (wakeLock?.isHeld == true) { if (wakeLock?.isHeld == true) {
wakeLock?.release() wakeLock?.release()
} }
+2
Просмотреть файл
@@ -48,4 +48,6 @@
<string name="hidden_alert">The app has been hidden. To open it again please dial 8722227 (TRACCAR).</string> <string name="hidden_alert">The app has been hidden. To open it again please dial 8722227 (TRACCAR).</string>
<string name="error_msg_invalid_url">Please enter a valid http:// or https:// URL</string> <string name="error_msg_invalid_url">Please enter a valid http:// or https:// URL</string>
<string name="request_exception">To continuously collect location data please turn off battery optimization for the app.</string> <string name="request_exception">To continuously collect location data please turn off battery optimization for the app.</string>
<string name="request_background">To continuously collect location data please enable \"%s\" permission.</string>
<string name="request_background_option">Allow all the time</string>
</resources> </resources>
+3 -3
Просмотреть файл
@@ -1,13 +1,13 @@
buildscript { buildscript {
ext.kotlin_version = '1.5.31' ext.kotlin_version = '1.6.10'
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.0.2' classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.google.gms:google-services:4.3.10' classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.7.1' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
} }
} }
+1 -1
Просмотреть файл
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip