From 66d0214cd9a86022420a8aa6cc65097c5d883a81 Mon Sep 17 00:00:00 2001 From: Tommaso Berlose Date: Sun, 3 Jan 2021 17:23:22 +0100 Subject: [PATCH] Move the location updates to a foreground service and fix multiple bugs --- .idea/caches/build_file_checksums.ser | Bin 537 -> 537 bytes app/build.gradle | 28 ++-- app/src/main/AndroidManifest.xml | 10 +- .../components/BottomSheetColorPicker.kt | 2 + .../anotherwidget/helpers/WeatherHelper.kt | 23 +--- .../anotherwidget/services/LocationService.kt | 126 ++++++++++++++++++ .../ui/activities/CustomLocationActivity.kt | 2 +- .../ui/fragments/MainFragment.kt | 2 +- .../ui/fragments/WeatherTabFragment.kt | 9 +- .../anotherwidget/utils/Extensions.kt | 1 + .../drawable-hdpi/ic_stat_notification.png | Bin 0 -> 674 bytes .../drawable-mdpi/ic_stat_notification.png | Bin 0 -> 381 bytes .../drawable-xhdpi/ic_stat_notification.png | Bin 0 -> 796 bytes .../drawable-xxhdpi/ic_stat_notification.png | Bin 0 -> 1373 bytes .../drawable-xxxhdpi/ic_stat_notification.png | Bin 0 -> 1739 bytes .../res/layout/color_picker_menu_item.xml | 1 + app/src/main/res/values/strings.xml | 6 + build.gradle | 6 +- 18 files changed, 169 insertions(+), 47 deletions(-) create mode 100644 app/src/main/java/com/tommasoberlose/anotherwidget/services/LocationService.kt create mode 100644 app/src/main/res/drawable-hdpi/ic_stat_notification.png create mode 100644 app/src/main/res/drawable-mdpi/ic_stat_notification.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_stat_notification.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_stat_notification.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_stat_notification.png diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index ac2541a77c10c45d3662d5ae582ab0c502a5d38e..55b326c63a90f6475179334f21327e7c4373243c 100644 GIT binary patch delta 54 zcmV-60LlNE1epYomj!mn%ecLgBa$I6S7O98zR0yDFynB;-~ MQ*|Q^BwhL~c$r%l+yDRo delta 54 zcmV-60LlNE1epYomj!ie4Rwx@oSzW(ZL?#zlXAbh*g#>pwGi2pO98zRoDBVOig?`u MUeskVx;zmTc&>&Ru>b%7 diff --git a/app/build.gradle b/app/build.gradle index 85c746d..0fe4eaf 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,15 +16,15 @@ apikeyProperties.load(new FileInputStream(apikeyPropertiesFile)) android { - compileSdkVersion 29 + compileSdkVersion 30 buildToolsVersion "29.0.3" defaultConfig { applicationId "com.tommasoberlose.anotherwidget" minSdkVersion 23 - targetSdkVersion 29 - versionCode 110 - versionName "2.0.15" + targetSdkVersion 30 + versionCode 112 + versionName "2.1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" buildConfigField("String", "GOOGLE_API_KEY", apikeyProperties['GOOGLE_API_KEY']) @@ -72,9 +72,9 @@ dependencies { // UI implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.2' - implementation 'com.google.android.material:material:1.3.0-alpha03' - implementation 'androidx.browser:browser:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' + implementation 'com.google.android.material:material:1.3.0-beta01' + implementation 'androidx.browser:browser:1.3.0' implementation 'net.idik:slimadapter:2.1.2' implementation 'com.google.android:flexbox:2.0.1' @@ -89,8 +89,8 @@ dependencies { implementation 'org.greenrobot:eventbus:3.2.0' // Navigation - implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1' - implementation 'androidx.navigation:navigation-ui-ktx:2.3.1' + implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2' + implementation 'androidx.navigation:navigation-ui-ktx:2.3.2' // Other implementation 'androidx.multidex:multidex:2.0.1' @@ -103,7 +103,7 @@ dependencies { kapt 'com.github.bumptech.glide:compiler:4.11.0' // Fitness - implementation 'com.google.android.gms:play-services-fitness:18.0.0' + implementation 'com.google.android.gms:play-services-fitness:20.0.0' implementation 'com.google.android.gms:play-services-auth:18.1.0' //Weather @@ -111,8 +111,8 @@ dependencies { implementation 'com.google.android.gms:play-services-location:17.1.0' // Billing - implementation 'com.android.billingclient:billing:3.0.1' - implementation 'com.android.billingclient:billing-ktx:3.0.1' + implementation 'com.android.billingclient:billing:3.0.2' + implementation 'com.android.billingclient:billing-ktx:3.0.2' // KTX implementation "androidx.core:core-ktx:1.3.2" @@ -128,10 +128,10 @@ dependencies { implementation "com.github.haroldadmin:NetworkResponseAdapter:4.0.1" //Coroutines - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9' // Add the Firebase SDK for Crashlytics. - implementation 'com.google.firebase:firebase-crashlytics:17.2.2' + implementation 'com.google.firebase:firebase-crashlytics:17.3.0' // Preferences implementation 'com.chibatching.kotpref:kotpref:2.11.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2a0fd53..61c0ca5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,7 +5,6 @@ - @@ -13,6 +12,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/components/BottomSheetColorPicker.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/components/BottomSheetColorPicker.kt index 68c8122..9ba5e64 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/components/BottomSheetColorPicker.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/components/BottomSheetColorPicker.kt @@ -24,6 +24,7 @@ import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.helpers.ColorHelper import com.tommasoberlose.anotherwidget.helpers.ColorHelper.isColorDark import com.tommasoberlose.anotherwidget.utils.expand +import com.tommasoberlose.anotherwidget.utils.isDarkTheme import com.tommasoberlose.anotherwidget.utils.reveal import com.tommasoberlose.anotherwidget.utils.toPixel import com.warkiz.widget.IndicatorSeekBar @@ -93,6 +94,7 @@ class BottomSheetColorPicker( injector .with(R.id.color) { it.setCardBackgroundColor(ColorStateList.valueOf(item)) + it.strokeWidth = if ((colors.indexOf(item) == 0 && !context.isDarkTheme()) || (colors.indexOf(item) == 10 && context.isDarkTheme())) 2 else 0 } .with(R.id.check) { if (getSelected?.invoke() == item) { diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt index 5379f61..59241ba 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/helpers/WeatherHelper.kt @@ -10,6 +10,7 @@ import com.tommasoberlose.anotherwidget.R import com.tommasoberlose.anotherwidget.global.Constants import com.tommasoberlose.anotherwidget.global.Preferences import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi +import com.tommasoberlose.anotherwidget.services.LocationService import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment import com.tommasoberlose.anotherwidget.ui.widgets.MainWidget import com.tommasoberlose.anotherwidget.utils.checkGrantedPermission @@ -31,26 +32,8 @@ object WeatherHelper { val networkApi = WeatherNetworkApi(context) if (Preferences.customLocationAdd != "") { networkApi.updateWeather() - } else if (context.checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION)) { - LocationServices.getFusedLocationProviderClient(context).lastLocation.addOnCompleteListener { task -> - if (task.isSuccessful) { - val location = task.result - if (location != null) { - Preferences.customLocationLat = location.latitude.toString() - Preferences.customLocationLon = location.longitude.toString() - } - - CoroutineScope(Dispatchers.IO).launch { - networkApi.updateWeather() - } - EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) - } else { - CoroutineScope(Dispatchers.IO).launch { - networkApi.updateWeather() - } - EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) - } - } + } else if (context.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) { + LocationService.requestNewLocation(context) } } diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/services/LocationService.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/services/LocationService.kt new file mode 100644 index 0000000..3281f6d --- /dev/null +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/services/LocationService.kt @@ -0,0 +1,126 @@ +package com.tommasoberlose.anotherwidget.services + +import android.Manifest +import android.app.* +import android.app.job.JobScheduler +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.location.Address +import android.location.Geocoder +import android.os.IBinder +import android.util.Log +import androidx.core.app.* +import androidx.core.content.ContextCompat +import com.google.android.gms.location.LocationServices +import com.tommasoberlose.anotherwidget.R +import com.tommasoberlose.anotherwidget.global.Preferences +import com.tommasoberlose.anotherwidget.network.WeatherNetworkApi +import com.tommasoberlose.anotherwidget.ui.activities.MainActivity +import com.tommasoberlose.anotherwidget.ui.fragments.MainFragment +import kotlinx.coroutines.* +import org.greenrobot.eventbus.EventBus +import java.lang.Exception +import java.util.* +import kotlin.collections.ArrayList + +class LocationService : Service() { + + private val jobs: ArrayList = ArrayList() + + override fun onCreate() { + super.onCreate() + + if (ActivityCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + ) { + startForeground(LOCATION_ACCESS_NOTIFICATION_ID, getLocationAccessNotification()) + + jobs += GlobalScope.launch(Dispatchers.IO) { + LocationServices.getFusedLocationProviderClient(this@LocationService).lastLocation.addOnCompleteListener { task -> + val networkApi = WeatherNetworkApi(this@LocationService) + if (task.isSuccessful) { + val location = task.result + if (location != null) { + Preferences.customLocationLat = location.latitude.toString() + Preferences.customLocationLon = location.longitude.toString() + } + + CoroutineScope(Dispatchers.IO).launch { + networkApi.updateWeather() + withContext(Dispatchers.Main) { + stopForeground(true) + } + } + EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) + } else { + CoroutineScope(Dispatchers.IO).launch { + networkApi.updateWeather() + withContext(Dispatchers.Main) { + stopForeground(true) + } + } + EventBus.getDefault().post(MainFragment.UpdateUiMessageEvent()) + } + } + } + } else { + stopForeground(true) + } + } + + override fun onDestroy() { + super.onDestroy() + jobs.forEach { + it.cancel() + } + } + + companion object { + const val LOCATION_ACCESS_NOTIFICATION_ID = 28465 + + @JvmStatic + fun requestNewLocation(context: Context) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + context.startForegroundService(Intent(context, LocationService::class.java)) + } else { + context.startService(Intent(context, LocationService::class.java)) + } + } + } + + override fun onBind(intent: Intent?): IBinder? { + return null + } + + private fun getLocationAccessNotification(): Notification { + with(NotificationManagerCompat.from(this)) { + // Create channel + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + createNotificationChannel( + NotificationChannel( + getString(R.string.location_access_notification_channel_id), + getString(R.string.location_access_notification_channel_name), + NotificationManager.IMPORTANCE_LOW + ).apply { + description = getString(R.string.location_access_notification_channel_description) + } + ) + } + + val builder = NotificationCompat.Builder(this@LocationService, getString(R.string.location_access_notification_channel_id)) + .setSmallIcon(R.drawable.ic_stat_notification) + .setContentTitle(getString(R.string.location_access_notification_title)) + .setStyle(NotificationCompat.BigTextStyle().bigText(getString(R.string.location_access_notification_subtitle))) + .setOngoing(true) + .setColor(ContextCompat.getColor(this@LocationService, R.color.colorAccent)) + + // Main intent that open the activity + builder.setContentIntent(PendingIntent.getActivity(this@LocationService, 0, Intent(this@LocationService, MainActivity::class.java), PendingIntent.FLAG_UPDATE_CURRENT)) + + return builder.build() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/CustomLocationActivity.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/CustomLocationActivity.kt index 7908d69..659b986 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/CustomLocationActivity.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/activities/CustomLocationActivity.kt @@ -140,7 +140,7 @@ class CustomLocationActivity : AppCompatActivity() { private fun requirePermission() { Dexter.withContext(this) .withPermissions( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION + Manifest.permission.ACCESS_FINE_LOCATION ).withListener(object: MultiplePermissionsListener { override fun onPermissionsChecked(report: MultiplePermissionsReport?) { report?.let { diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt index 9155218..dbd115a 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/MainFragment.kt @@ -351,7 +351,7 @@ class MainFragment : Fragment(), SharedPreferences.OnSharedPreferenceChangeList }?.isVisible = if (Preferences.showWeather) { (WeatherHelper.isKeyRequired() && WeatherHelper.getApiKey() == "") || (Preferences.customLocationAdd == "" && activity?.checkGrantedPermission( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION + Manifest.permission.ACCESS_FINE_LOCATION ) != true) || (Preferences.weatherProviderError != "" && Preferences.weatherProviderError != "-") || (Preferences.weatherProviderLocationError != "") diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/WeatherTabFragment.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/WeatherTabFragment.kt index a71faf2..c71c580 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/WeatherTabFragment.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/ui/fragments/WeatherTabFragment.kt @@ -161,12 +161,7 @@ class WeatherTabFragment : Fragment() { } private fun checkLocationPermission() { - // Background permission - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && activity?.checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION) == true && activity?.checkGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != true) { - requirePermission() - } - - if (activity?.checkGrantedPermission(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION) == true) { + if (requireActivity().checkGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION)) { location_permission_alert?.isVisible = false background_location_warning.isVisible = Preferences.customLocationAdd == "" WeatherReceiver.setUpdates(requireContext()) @@ -299,7 +294,7 @@ class WeatherTabFragment : Fragment() { private fun requirePermission() { Dexter.withContext(requireContext()) .withPermissions( - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) Manifest.permission.ACCESS_BACKGROUND_LOCATION else Manifest.permission.ACCESS_FINE_LOCATION + Manifest.permission.ACCESS_FINE_LOCATION ).withListener(object: MultiplePermissionsListener { override fun onPermissionsChecked(report: MultiplePermissionsReport?) { report?.let { diff --git a/app/src/main/java/com/tommasoberlose/anotherwidget/utils/Extensions.kt b/app/src/main/java/com/tommasoberlose/anotherwidget/utils/Extensions.kt index 7f7f9f3..750655b 100644 --- a/app/src/main/java/com/tommasoberlose/anotherwidget/utils/Extensions.kt +++ b/app/src/main/java/com/tommasoberlose/anotherwidget/utils/Extensions.kt @@ -21,6 +21,7 @@ import android.content.Intent import android.content.res.Resources import android.graphics.drawable.Drawable import android.util.DisplayMetrics +import android.util.Log import android.util.TypedValue import android.view.ViewPropertyAnimator import android.view.animation.Animation diff --git a/app/src/main/res/drawable-hdpi/ic_stat_notification.png b/app/src/main/res/drawable-hdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..c5b1cae4b876bc2f62b55ab9e819e200db72f389 GIT binary patch literal 674 zcmV;T0$u%yP)L%6vuzdSs}*x9A!-0C@$QP)4dNsxxfc-C^zopV$2jDz}#`~12`2$AyfidcnmxPUYObY@+0m(C4A6R@s;+ zSJ-#p32+U#2;7K+wZLXz9k2jUoWec>_kgRw1>laEDb6q(0m$7a0Na2aKrbX`)l=YH z+|v)3mq^PG;4W}VepR-*0aDPzz|;&ddRsB310Jc-cgl3<9dN|V#8$_K0dn^xzz{Ga z6|r=#x|$z}&;jJ`lY!%r|J;D%fTh6raQF&50KVl0h&tSFW@?t- z_2KRtfWb;3rsB>7W>+q$lU}AAOXn)=17>!)3P^RcJ+p2K-{mECiZ|5K_oJqAaH*co znAy<|Ab0Npjsa_OBE%haqtaDwZ5=6HN}JM`Yd6hoZyC^P;1IAXHv*sms97_n;#171 z52J{6%glB)8jy;!zcDEjRqo!Lq7FbCfr0Q=i45kv@fl z6*F}bRzhtdAPM`nB4MQ-yIT8yS*lA~hFXSF45ZH68cH$r69@puB}Ri1Gynhq07*qo IM6N<$f*_4J0ssI2 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_stat_notification.png b/app/src/main/res/drawable-mdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..31e583e1a0d27adc97ad0ba5aca0f819966df41a GIT binary patch literal 381 zcmV-@0fPRCP)b@^ufYp*pT@loRZbOE&rca+G?hJZ=n6}XgS#cXEgvD3gHa4qSjiq-}& zvmRg>SOi8O@6j7@1#E;QyCL~Ncz5^%a10zu`p#fEz|8uA9bh(6xBItMQsH|JG$ehv ztq52J{ONRhf$?Cx1->G_O-aohU}mGhSrJ$fJPdeW{5%6s5r9`YC+WUbftk$%n>Er* zlZk_QUuTkjFs$nz-J`pNYehl0a8e`E$JkyAZ?RFqtji}0PsQc37x6A zO)d9`&xKF?uR#kCUll9BLTOdJhgGp267K<1Kyg((hE;J;xhl$Elw{dO%Ko#d`$wyl bF1bWMrs{MVbKvr000000NkvXXu0mjfzt*F1 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_stat_notification.png b/app/src/main/res/drawable-xhdpi/ic_stat_notification.png new file mode 100644 index 0000000000000000000000000000000000000000..8a1deaab56e7d4714ce65e7754e6d9c26a8b0c90 GIT binary patch literal 796 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 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index abb9602..dde1815 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -161,6 +161,12 @@ Select a provider Select a weather provider from the list.\nA few providers need a free personal account,\nbut they are usually more accurate. + location-access + Background service + Service used to update the weather based on the current location of the user. + Weather update + We\'re updating the weather based on your current location. + Clock Tap on clock opens diff --git a/build.gradle b/build.gradle index dcf52b7..19b085b 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.4.10' + ext.kotlin_version = '1.4.21' repositories { google() jcenter() @@ -10,12 +10,12 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:4.1.0' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.google.gms:google-services:4.3.4' // Add the Crashlytics Gradle plugin. - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1' classpath 'io.realm:realm-gradle-plugin:6.0.2'