From ed8cb50b08073fbb4740eb7c0c92101d5fc366c3 Mon Sep 17 00:00:00 2001 From: johan12345 Date: Sun, 12 Sep 2021 18:18:33 +0200 Subject: [PATCH 1/4] increase targetSdk to Android 12 (API 31) (#123) --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 6a8477c2..f0af5aa2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -12,7 +12,7 @@ android { defaultConfig { applicationId "net.vonforst.evmap" minSdkVersion 21 - targetSdkVersion 30 + targetSdkVersion 31 versionCode 61 versionName "1.0.0-beta03" From 98b695ed4b76a6eb7048297b454d20e4eebde0e0 Mon Sep 17 00:00:00 2001 From: johan12345 Date: Sun, 12 Sep 2021 18:19:19 +0200 Subject: [PATCH 2/4] Android 12 compat: set exported attribute explicitly (#123) for services and activities in Manifest --- app/src/google/AndroidManifest.xml | 3 ++- app/src/main/AndroidManifest.xml | 13 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/google/AndroidManifest.xml b/app/src/google/AndroidManifest.xml index 3e6a6b3c..cebc579e 100644 --- a/app/src/google/AndroidManifest.xml +++ b/app/src/google/AndroidManifest.xml @@ -46,6 +46,7 @@ + android:enabled="true" + android:exported="false" /> \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3158f168..9dc09ab1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -32,7 +32,8 @@ + android:theme="@style/AppTheme.LaunchScreen" + android:exported="true"> @@ -256,6 +257,16 @@ + + + + + + + \ No newline at end of file From ab93577a981e47aaed2880f1cd95127b8f136db1 Mon Sep 17 00:00:00 2001 From: johan12345 Date: Sun, 12 Sep 2021 18:42:20 +0200 Subject: [PATCH 3/4] Android 12 compat: support for approximate location permission (#123) --- .../net/vonforst/evmap/auto/CarAppService.kt | 9 +--- .../net/vonforst/evmap/auto/WelcomeScreen.kt | 5 ++- app/src/main/AndroidManifest.xml | 1 + .../evmap/fragment/FavoritesFragment.kt | 10 +---- .../vonforst/evmap/fragment/MapFragment.kt | 41 ++++++++----------- .../net/vonforst/evmap/utils/LocationUtils.kt | 18 ++++++++ 6 files changed, 43 insertions(+), 41 deletions(-) diff --git a/app/src/google/java/net/vonforst/evmap/auto/CarAppService.kt b/app/src/google/java/net/vonforst/evmap/auto/CarAppService.kt index 8463c336..782e3719 100644 --- a/app/src/google/java/net/vonforst/evmap/auto/CarAppService.kt +++ b/app/src/google/java/net/vonforst/evmap/auto/CarAppService.kt @@ -1,9 +1,7 @@ package net.vonforst.evmap.auto -import android.Manifest import android.content.* import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager import android.location.Location import android.os.IBinder import androidx.car.app.CarContext @@ -18,6 +16,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.localbroadcastmanager.content.LocalBroadcastManager +import net.vonforst.evmap.utils.checkAnyLocationPermission interface LocationAwareScreen { @@ -72,11 +71,7 @@ class EVMapSession(val cas: CarAppService) : Session(), LifecycleObserver { return WelcomeScreen(carContext, this) } - fun locationPermissionGranted() = - ContextCompat.checkSelfPermission( - carContext, - Manifest.permission.ACCESS_FINE_LOCATION - ) == PackageManager.PERMISSION_GRANTED + fun locationPermissionGranted() = carContext.checkAnyLocationPermission() private val locationReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { diff --git a/app/src/google/java/net/vonforst/evmap/auto/WelcomeScreen.kt b/app/src/google/java/net/vonforst/evmap/auto/WelcomeScreen.kt index aa8b3632..239bf88f 100644 --- a/app/src/google/java/net/vonforst/evmap/auto/WelcomeScreen.kt +++ b/app/src/google/java/net/vonforst/evmap/auto/WelcomeScreen.kt @@ -23,7 +23,10 @@ class WelcomeScreen(ctx: CarContext, val session: EVMapSession) : Screen(ctx), L PermissionScreen( carContext, R.string.auto_location_permission_needed, - listOf(Manifest.permission.ACCESS_FINE_LOCATION) + listOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) ) ) { session.bindLocationService() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9dc09ab1..83ec18ac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="net.vonforst.evmap"> + diff --git a/app/src/main/java/net/vonforst/evmap/fragment/FavoritesFragment.kt b/app/src/main/java/net/vonforst/evmap/fragment/FavoritesFragment.kt index 492c5fb3..60ee6746 100644 --- a/app/src/main/java/net/vonforst/evmap/fragment/FavoritesFragment.kt +++ b/app/src/main/java/net/vonforst/evmap/fragment/FavoritesFragment.kt @@ -1,7 +1,5 @@ package net.vonforst.evmap.fragment -import android.Manifest -import android.content.pm.PackageManager import android.graphics.Canvas import android.os.Bundle import android.view.Gravity @@ -9,7 +7,6 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout -import androidx.core.content.ContextCompat import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels @@ -30,6 +27,7 @@ import net.vonforst.evmap.adapter.FavoritesAdapter import net.vonforst.evmap.databinding.FragmentFavoritesBinding import net.vonforst.evmap.databinding.ItemFavoriteBinding import net.vonforst.evmap.model.ChargeLocation +import net.vonforst.evmap.utils.checkAnyLocationPermission import net.vonforst.evmap.viewmodel.FavoritesViewModel import net.vonforst.evmap.viewmodel.viewModelFactory @@ -100,11 +98,7 @@ class FavoritesFragment : Fragment(), LostApiClient.ConnectionCallbacks { override fun onConnected() { val context = this.context ?: return - if (ContextCompat.checkSelfPermission( - context, - Manifest.permission.ACCESS_FINE_LOCATION - ) == PackageManager.PERMISSION_GRANTED - ) { + if (context.checkAnyLocationPermission()) { val location = LocationServices.FusedLocationApi.getLastLocation(locationClient!!) if (location != null) { vm.location.value = LatLng(location.latitude, location.longitude) diff --git a/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt b/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt index 6521f686..2f56ea40 100644 --- a/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt +++ b/app/src/main/java/net/vonforst/evmap/fragment/MapFragment.kt @@ -1,5 +1,6 @@ package net.vonforst.evmap.fragment +import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.annotation.SuppressLint import android.content.Context @@ -81,6 +82,8 @@ import net.vonforst.evmap.ui.ClusterIconGenerator import net.vonforst.evmap.ui.MarkerAnimator import net.vonforst.evmap.ui.getMarkerTint import net.vonforst.evmap.utils.boundingBox +import net.vonforst.evmap.utils.checkAnyLocationPermission +import net.vonforst.evmap.utils.checkFineLocationPermission import net.vonforst.evmap.utils.distanceBetween import net.vonforst.evmap.viewmodel.* import java.io.IOException @@ -262,10 +265,8 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac ) vm.reloadPrefs() - if (requestingLocationUpdates && ContextCompat.checkSelfPermission( - requireContext(), - ACCESS_FINE_LOCATION - ) == PackageManager.PERMISSION_GRANTED && locationClient.isConnected + if (requestingLocationUpdates && requireContext().checkAnyLocationPermission() + && locationClient.isConnected ) { requestLocationUpdates() } @@ -273,16 +274,14 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac private fun setupClickListeners() { binding.fabLocate.setOnClickListener { - if (ContextCompat.checkSelfPermission( - requireContext(), - ACCESS_FINE_LOCATION - ) != PackageManager.PERMISSION_GRANTED - ) { - requestPermissions( - arrayOf(ACCESS_FINE_LOCATION), + if (!requireContext().checkFineLocationPermission()) { + ActivityCompat.requestPermissions( + requireActivity(), + arrayOf(ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION), REQUEST_LOCATION_PERMISSION ) - } else { + } + if (requireContext().checkAnyLocationPermission()) { enableLocation(moveTo = true, animate = true) } } @@ -883,11 +882,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac } } } - if (ContextCompat.checkSelfPermission( - requireContext(), - ACCESS_FINE_LOCATION - ) == PackageManager.PERMISSION_GRANTED - ) { + if (context?.checkAnyLocationPermission() ?: false) { enableLocation(!positionSet, false) positionSet = true } @@ -903,7 +898,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac ) } - @RequiresPermission(ACCESS_FINE_LOCATION) + @RequiresPermission(anyOf = [ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION]) private fun enableLocation(moveTo: Boolean, animate: Boolean) { val map = this.map ?: return map.setMyLocationEnabled(true) @@ -917,7 +912,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac } } - @RequiresPermission(ACCESS_FINE_LOCATION) + @RequiresPermission(anyOf = [ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION]) private fun moveToLastLocation(map: AnyMap, animate: Boolean) { val location = LocationServices.FusedLocationApi.getLastLocation(locationClient) if (location != null) { @@ -1027,7 +1022,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac ) { when (requestCode) { REQUEST_LOCATION_PERMISSION -> { - if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { + if ((grantResults.isNotEmpty() && grantResults.any { it == PackageManager.PERMISSION_GRANTED })) { enableLocation(moveTo = true, animate = true) } } @@ -1200,11 +1195,7 @@ class MapFragment : Fragment(), OnMapReadyCallback, MapsActivity.FragmentCallbac val map = this.map ?: return val context = this.context ?: return if (vm.myLocationEnabled.value == true) { - if (ActivityCompat.checkSelfPermission( - context, - ACCESS_FINE_LOCATION - ) == PackageManager.PERMISSION_GRANTED - ) { + if (context.checkAnyLocationPermission()) { moveToLastLocation(map, false) requestLocationUpdates() } diff --git a/app/src/main/java/net/vonforst/evmap/utils/LocationUtils.kt b/app/src/main/java/net/vonforst/evmap/utils/LocationUtils.kt index e8f79569..ae13608d 100644 --- a/app/src/main/java/net/vonforst/evmap/utils/LocationUtils.kt +++ b/app/src/main/java/net/vonforst/evmap/utils/LocationUtils.kt @@ -1,7 +1,11 @@ package net.vonforst.evmap.utils +import android.Manifest +import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.location.Location +import androidx.core.content.ContextCompat import com.car2go.maps.model.LatLng import com.car2go.maps.model.LatLngBounds import kotlin.math.* @@ -75,3 +79,17 @@ fun boundingBox(pos: LatLng, sizeMeters: Double): LatLngBounds { pos.plusMeters(sizeMeters, sizeMeters) ) } + +fun Context.checkAnyLocationPermission() = ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION +) == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + +fun Context.checkFineLocationPermission() = ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION +) == PackageManager.PERMISSION_GRANTED \ No newline at end of file From 6302006a35139f07720d59318387b07c851e1d0a Mon Sep 17 00:00:00 2001 From: johan12345 Date: Sun, 12 Sep 2021 20:57:37 +0200 Subject: [PATCH 4/4] Android 12 compat: implement new SplashScreen API with animated icon update onboarding to avoid showing animation twice --- app/build.gradle | 1 + .../java/net/vonforst/evmap/MapsActivity.kt | 24 +- .../evmap/fragment/OnboardingFragment.kt | 12 +- .../res/drawable/ic_appicon_splashscreen.xml | 49 ---- app/src/main/res/drawable/intro_anim.xml | 217 ++++++++++++++++++ .../main/res/drawable/intro_anim_finished.xml | 121 ++++++++++ app/src/main/res/drawable/intro_static.xml | 120 ++++++++++ app/src/main/res/drawable/launch_screen.xml | 8 - .../layout/fragment_onboarding_welcome.xml | 7 +- app/src/main/res/values-night/colors.xml | 4 + app/src/main/res/values-v31/drawables.xml | 4 + app/src/main/res/values-v31/styles.xml | 7 + app/src/main/res/values/colors.xml | 1 + app/src/main/res/values/drawables.xml | 4 + app/src/main/res/values/styles.xml | 10 +- 15 files changed, 521 insertions(+), 68 deletions(-) delete mode 100644 app/src/main/res/drawable/ic_appicon_splashscreen.xml create mode 100644 app/src/main/res/drawable/intro_anim.xml create mode 100644 app/src/main/res/drawable/intro_anim_finished.xml create mode 100644 app/src/main/res/drawable/intro_static.xml delete mode 100644 app/src/main/res/drawable/launch_screen.xml create mode 100644 app/src/main/res/values-night/colors.xml create mode 100644 app/src/main/res/values-v31/drawables.xml create mode 100644 app/src/main/res/values-v31/styles.xml create mode 100644 app/src/main/res/values/drawables.xml diff --git a/app/build.gradle b/app/build.gradle index f0af5aa2..dcb832e5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -110,6 +110,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'androidx.core:core-ktx:1.5.0' + implementation 'androidx.core:core-splashscreen:1.0.0-alpha01' implementation "androidx.activity:activity-ktx:1.2.3" implementation "androidx.fragment:fragment-ktx:1.3.4" implementation 'androidx.cardview:cardview:1.0.0' diff --git a/app/src/main/java/net/vonforst/evmap/MapsActivity.kt b/app/src/main/java/net/vonforst/evmap/MapsActivity.kt index 8de131ae..bf8ffcb1 100644 --- a/app/src/main/java/net/vonforst/evmap/MapsActivity.kt +++ b/app/src/main/java/net/vonforst/evmap/MapsActivity.kt @@ -4,12 +4,16 @@ import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent import android.net.Uri +import android.os.Build import android.os.Bundle +import android.os.SystemClock import android.view.View import androidx.appcompat.app.AppCompatActivity import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.ContextCompat +import androidx.core.splashscreen.SplashScreen +import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.drawerlayout.widget.DrawerLayout @@ -51,9 +55,8 @@ class MapsActivity : AppCompatActivity() { } override fun onCreate(savedInstanceState: Bundle?) { - // set theme to AppTheme to end launch screen - setTheme(R.style.AppTheme) super.onCreate(savedInstanceState) + val splashScreen = installSplashScreen() setContentView(R.layout.activity_maps) @@ -82,8 +85,23 @@ class MapsActivity : AppCompatActivity() { checkPlayServices(this) - if (!prefs.welcomeDialogShown || !prefs.dataSourceSet) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // wait for splash screen animation to finish on first start + splashScreen.setKeepVisibleCondition(object : SplashScreen.KeepOnScreenCondition { + var startTime: Long? = null + + override fun shouldKeepOnScreen(): Boolean { + val st = startTime + if (st == null) { + startTime = SystemClock.uptimeMillis() + return true + } else { + return (SystemClock.uptimeMillis() - st) < 1000 + } + } + }) + } navGraph.startDestination = R.id.onboarding navController.graph = navGraph return diff --git a/app/src/main/java/net/vonforst/evmap/fragment/OnboardingFragment.kt b/app/src/main/java/net/vonforst/evmap/fragment/OnboardingFragment.kt index b12c8945..0bd33526 100644 --- a/app/src/main/java/net/vonforst/evmap/fragment/OnboardingFragment.kt +++ b/app/src/main/java/net/vonforst/evmap/fragment/OnboardingFragment.kt @@ -4,11 +4,13 @@ import android.animation.AnimatorSet import android.animation.ObjectAnimator import android.annotation.SuppressLint import android.content.Context +import android.graphics.drawable.AnimatedVectorDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.animation.DecelerateInterpolator +import android.widget.ImageView import androidx.fragment.app.Fragment import androidx.navigation.fragment.findNavController import androidx.viewpager2.widget.ViewPager2 @@ -93,12 +95,18 @@ class WelcomeFragment : OnboardingPageFragment() { override fun onResume() { super.onResume() - binding.animationView.playAnimation() + val drawable = (binding.animationView as ImageView).drawable + if (drawable is AnimatedVectorDrawable) { + drawable.start() + } } override fun onPause() { super.onPause() - binding.animationView.progress = 0f + val drawable = (binding.animationView as ImageView).drawable + if (drawable is AnimatedVectorDrawable) { + drawable.stop() + } } } diff --git a/app/src/main/res/drawable/ic_appicon_splashscreen.xml b/app/src/main/res/drawable/ic_appicon_splashscreen.xml deleted file mode 100644 index cc15ae59..00000000 --- a/app/src/main/res/drawable/ic_appicon_splashscreen.xml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/intro_anim.xml b/app/src/main/res/drawable/intro_anim.xml new file mode 100644 index 00000000..88210c8c --- /dev/null +++ b/app/src/main/res/drawable/intro_anim.xml @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/intro_anim_finished.xml b/app/src/main/res/drawable/intro_anim_finished.xml new file mode 100644 index 00000000..7f591963 --- /dev/null +++ b/app/src/main/res/drawable/intro_anim_finished.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/intro_static.xml b/app/src/main/res/drawable/intro_static.xml new file mode 100644 index 00000000..0fdf5fff --- /dev/null +++ b/app/src/main/res/drawable/intro_static.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/launch_screen.xml b/app/src/main/res/drawable/launch_screen.xml deleted file mode 100644 index 06e1b656..00000000 --- a/app/src/main/res/drawable/launch_screen.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_onboarding_welcome.xml b/app/src/main/res/layout/fragment_onboarding_welcome.xml index d96054fd..33bddf64 100644 --- a/app/src/main/res/layout/fragment_onboarding_welcome.xml +++ b/app/src/main/res/layout/fragment_onboarding_welcome.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + app:srcCompat="@drawable/intro_anim_onboarding" + android:contentDescription="@string/app_name" /> + + #121212 + \ No newline at end of file diff --git a/app/src/main/res/values-v31/drawables.xml b/app/src/main/res/values-v31/drawables.xml new file mode 100644 index 00000000..df61df1a --- /dev/null +++ b/app/src/main/res/values-v31/drawables.xml @@ -0,0 +1,4 @@ + + + @drawable/intro_anim_finished + \ No newline at end of file diff --git a/app/src/main/res/values-v31/styles.xml b/app/src/main/res/values-v31/styles.xml new file mode 100644 index 00000000..71b76182 --- /dev/null +++ b/app/src/main/res/values-v31/styles.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f5ac3830..1c8c9550 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -22,4 +22,5 @@ #546E7A #00C853 #1F000000 + #FFFFFF diff --git a/app/src/main/res/values/drawables.xml b/app/src/main/res/values/drawables.xml new file mode 100644 index 00000000..636a49c5 --- /dev/null +++ b/app/src/main/res/values/drawables.xml @@ -0,0 +1,4 @@ + + + @drawable/intro_anim + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 24e03b96..a7e18a56 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -17,8 +17,14 @@ + +