Skip to content

Commit b0f45f7

Browse files
committed
Update to targetSdk 35 and fix missing Notification
We need to urgently update to API 35, so the app will remain available on play store. At the same time we had some issues on newer API Versions not showing the service notification. This lead to people leaving tracking on unintentionally. This has been fixed. To simplify permission handling, we handle location and notification access together.
1 parent 380fc8f commit b0f45f7

File tree

9 files changed

+92
-41
lines changed

9 files changed

+92
-41
lines changed

app/build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ android {
2121
}
2222
}
2323

24-
compileSdk 33
24+
compileSdk 35
2525

2626
defaultConfig {
2727
applicationId "de.stephanlindauer.criticalmaps"
2828
minSdkVersion 16
29-
targetSdkVersion 33
29+
targetSdkVersion 35
3030
versionCode 48
3131
versionName "2.9.0"
3232
vectorDrawables.useSupportLibrary = true
@@ -78,6 +78,7 @@ dependencies {
7878
implementation 'com.squareup:otto:1.3.8'
7979
implementation 'org.osmdroid:osmdroid-android:6.1.8'
8080
implementation 'com.squareup.picasso:picasso:2.8'
81+
implementation 'androidx.core:core:1.12.0'
8182
implementation 'androidx.appcompat:appcompat:1.6.1'
8283
implementation 'androidx.annotation:annotation:1.9.1'
8384
implementation 'com.google.android.material:material:1.9.0'

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
1212
android:maxSdkVersion="18" />
1313
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
14+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
15+
<uses-permission
16+
android:name="android.permission.POST_NOTIFICATIONS"
17+
android:minSdkVersion="33" />
1418

1519
<uses-feature
1620
android:name="android.hardware.location.network"

app/src/main/java/de/stephanlindauer/criticalmaps/Main.java

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import android.animation.ArgbEvaluator;
44
import android.animation.ValueAnimator;
5-
import android.annotation.TargetApi;
65
import android.app.Activity;
76
import android.content.Intent;
87
import android.content.SharedPreferences;
@@ -23,6 +22,7 @@
2322

2423
import androidx.annotation.IdRes;
2524
import androidx.annotation.NonNull;
25+
import androidx.annotation.RequiresApi;
2626
import androidx.appcompat.app.ActionBarDrawerToggle;
2727
import androidx.appcompat.app.AppCompatActivity;
2828
import androidx.appcompat.widget.SwitchCompat;
@@ -82,7 +82,7 @@ public class Main extends AppCompatActivity implements NavigationView.OnNavigati
8282
setKeepScreenOn();
8383
break;
8484
case SharedPrefsKeys.PRIVACY_POLICY_ACCEPTED:
85-
if (!locationUpdateManager.checkPermission()) {
85+
if (!LocationUpdateManager.checkPermission()) {
8686
locationUpdateManager.requestPermission();
8787
}
8888
break;
@@ -143,8 +143,6 @@ public void onCreate(Bundle bundle) {
143143

144144
setShowOnLockscreen();
145145
setKeepScreenOn();
146-
147-
ServerSyncService.startService();
148146
}
149147

150148
@Override
@@ -226,15 +224,26 @@ public void onDrawerClosed(@NonNull View drawerView) {
226224
navigateTo(R.id.navigation_map);
227225
}
228226

229-
final boolean isPrivacyPolicyAccepted =
230-
!privacyPolicyAcceptedPreference.isSet() || !privacyPolicyAcceptedPreference.get();
231-
if (isPrivacyPolicyAccepted) {
227+
locationUpdateManager.initialize();
228+
229+
final boolean isPrivacyPolicyAccepted = privacyPolicyAcceptedPreference.get();
230+
if (!isPrivacyPolicyAccepted) {
232231
binding.introductionText.setMovementMethod(LinkMovementMethod.getInstance());
233232
binding.introductionText.setText(Html.fromHtml(getString(R.string.introduction_gps)));
234233
binding.introductionView.setVisibility(View.VISIBLE);
235234
}
236235
}
237236

237+
@Override
238+
protected void onResume() {
239+
super.onResume();
240+
Timber.d("onResume() called");
241+
final boolean isPrivacyPolicyAccepted = privacyPolicyAcceptedPreference.get();
242+
if (isPrivacyPolicyAccepted) {
243+
initiateServiceStartIfPermitted();
244+
}
245+
}
246+
238247
@Override
239248
protected void onStop() {
240249
permissionCheckHandler.detachActivity();
@@ -454,7 +463,7 @@ private void animateToolbar(int durationMillis, boolean toMap) {
454463
radiusAnimator.start();
455464
}
456465

457-
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
466+
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
458467
private void fadeInStatusBarColor(int duration, boolean toMap) {
459468
int colorMap = ContextCompat.getColor(this, R.color.main_statusbarcolor_map);
460469
int colorOthers = ContextCompat.getColor(this, R.color.main_statusbarcolor_others);
@@ -484,4 +493,17 @@ private void handleObserverModeSwitchCheckedChanged(boolean isChecked) {
484493
new BooleanPreference(
485494
sharedPreferences, SharedPrefsKeys.OBSERVER_MODE_ACTIVE).set(isChecked);
486495
}
496+
497+
private void initiateServiceStartIfPermitted() {
498+
if (LocationUpdateManager.checkPermission()) {
499+
if (!ServerSyncService.isCurrentlyRunning()) {
500+
Timber.d("Location and notification permissions granted. Attempting to start ServerSyncService.");
501+
ServerSyncService.startService();
502+
} else {
503+
Timber.d("Location and notification permission granted, but service is already running.");
504+
}
505+
} else {
506+
Timber.d("Location and notification permission NOT granted. ServerSyncService will not be started.");
507+
}
508+
}
487509
}

app/src/main/java/de/stephanlindauer/criticalmaps/fragments/ChatFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ public void run() {
271271
getChatmessagesHandler.get().execute();
272272
}
273273
};
274-
timerGetChatmessages.scheduleAtFixedRate(timerTaskPullServer, 0, SERVER_SYNC_INTERVAL);
274+
timerGetChatmessages.schedule(timerTaskPullServer, 0, SERVER_SYNC_INTERVAL);
275275
}
276276

277277
private void stopGetChatmessagesTimer() {

app/src/main/java/de/stephanlindauer/criticalmaps/fragments/MapFragment.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import android.animation.AnimatorInflater;
44
import android.animation.ObjectAnimator;
5-
import android.annotation.TargetApi;
65
import android.content.Intent;
76
import android.content.SharedPreferences;
87
import android.graphics.drawable.Drawable;
@@ -19,6 +18,7 @@
1918
import androidx.annotation.ColorRes;
2019
import androidx.annotation.DrawableRes;
2120
import androidx.annotation.NonNull;
21+
import androidx.annotation.RequiresApi;
2222
import androidx.appcompat.app.AlertDialog;
2323
import androidx.appcompat.content.res.AppCompatResources;
2424
import androidx.core.content.ContextCompat;
@@ -256,9 +256,13 @@ public void onRotate(float deltaAngle) {
256256
binding.mapSetNorthFab.setRotation(mapView.getMapOrientation());
257257

258258
showGpxHandler.showGpx(mapView);
259+
260+
if (!LocationUpdateManager.checkPermission()) {
261+
zoomToLocation(defaultGeoPoint, NO_GPS_PERMISSION_ZOOM_LEVEL);
262+
}
259263
}
260264

261-
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
265+
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
262266
private void adjustToWindowsInsets() {
263267
// No-op on < API21
264268
ViewCompat.setOnApplyWindowInsetsListener(binding.mapOverlayContainerLayout, (v, insets) -> {
@@ -321,12 +325,6 @@ public void onResume() {
321325
sharedPreferences.registerOnSharedPreferenceChangeListener(
322326
observerModeOnSharedPreferenceChangeListener);
323327

324-
if (locationUpdateManager.checkPermission()) {
325-
locationUpdateManager.startListening();
326-
} else {
327-
zoomToLocation(defaultGeoPoint, NO_GPS_PERMISSION_ZOOM_LEVEL);
328-
}
329-
330328
startGetLocationTimer();
331329
}
332330

@@ -505,7 +503,7 @@ public void run() {
505503
getLocationHandler.get().execute();
506504
}
507505
};
508-
timerGetLocation.scheduleAtFixedRate(timerTaskPullServer, 0, SERVER_SYNC_INTERVAL);
506+
timerGetLocation.schedule(timerTaskPullServer, 0, SERVER_SYNC_INTERVAL);
509507
}
510508

511509
private void stopGetLocationTimer() {

app/src/main/java/de/stephanlindauer/criticalmaps/handler/PermissionCheckHandler.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void requestPermissionsWithRationaleIfNeeded(PermissionRequest permission
4242
activePermissionRequest = permissionRequest;
4343

4444
// short-circuit here if already granted
45-
if (checkPermissionsGranted(permissionRequest.getPermissions())) {
45+
if (checkAllPermissionsGranted(permissionRequest.getPermissions())) {
4646
activePermissionRequest.getOnGrantedCallback().run();
4747
activePermissionRequest = null;
4848
return;
@@ -91,7 +91,8 @@ public boolean handlePermissionRequestCallback(int requestCode, int[] grantResul
9191
return false;
9292
}
9393

94-
boolean allPermissionsGranted = true;
94+
// Note: Can be an empty array that should be treated as cancellation.
95+
boolean allPermissionsGranted = grantResults.length > 0;
9596
for (int result : grantResults) {
9697
allPermissionsGranted = allPermissionsGranted &&
9798
result == PackageManager.PERMISSION_GRANTED;
@@ -120,7 +121,7 @@ public boolean handlePermissionRequestCallback(int requestCode, int[] grantResul
120121
return true;
121122
}
122123

123-
public static boolean checkPermissionsGranted(String[] permissions) {
124+
public static boolean checkAllPermissionsGranted(String[] permissions) {
124125
boolean permissionsGranted = true;
125126
for (String permission : permissions) {
126127
permissionsGranted = permissionsGranted && (PackageManager.PERMISSION_GRANTED ==

app/src/main/java/de/stephanlindauer/criticalmaps/managers/LocationUpdateManager.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,14 @@
33
import android.Manifest;
44
import android.annotation.SuppressLint;
55
import android.content.Context;
6+
import android.content.pm.PackageManager;
67
import android.location.Location;
78
import android.location.LocationListener;
89
import android.location.LocationManager;
910
import android.os.Bundle;
1011

12+
import androidx.core.content.ContextCompat;
13+
1114
import com.squareup.otto.Produce;
1215

1316
import org.osmdroid.util.GeoPoint;
@@ -42,9 +45,12 @@ public class LocationUpdateManager {
4245
private final String[] USED_PROVIDERS = new String[]{
4346
LocationManager.GPS_PROVIDER,
4447
LocationManager.NETWORK_PROVIDER};
48+
49+
@SuppressLint("InlinedApi")
4550
private final String[] PERMISSIONS = {
4651
Manifest.permission.ACCESS_FINE_LOCATION,
47-
Manifest.permission.ACCESS_COARSE_LOCATION};
52+
Manifest.permission.ACCESS_COARSE_LOCATION,
53+
Manifest.permission.POST_NOTIFICATIONS};
4854
private final LocationManager locationManager;
4955
private Location lastPublishedLocation;
5056

@@ -143,7 +149,7 @@ public boolean isUpdating() {
143149
return isUpdating;
144150
}
145151

146-
public void initializeAndStartListening() {
152+
public void initialize() {
147153
boolean noProviderExists = !checkIfAtLeastOneProviderExits();
148154
boolean noPermission = !checkPermission();
149155

@@ -165,8 +171,6 @@ public void initializeAndStartListening() {
165171
if (noPermission) {
166172
return;
167173
}
168-
169-
startListening();
170174
}
171175

172176
public void startListening() {
@@ -203,8 +207,12 @@ private void requestLocationUpdatesIfProviderExists(String provider) {
203207
}
204208
}
205209

206-
public boolean checkPermission() {
207-
return PermissionCheckHandler.checkPermissionsGranted(PERMISSIONS);
210+
@SuppressLint("InlinedApi")
211+
public static boolean checkPermission() {
212+
App app = App.components().app();
213+
return (ContextCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
214+
|| ContextCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)
215+
&& ContextCompat.checkSelfPermission(app, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
208216
}
209217

210218
public void requestPermission() {

app/src/main/java/de/stephanlindauer/criticalmaps/service/ServerSyncService.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package de.stephanlindauer.criticalmaps.service;
22

3+
import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
4+
5+
import android.annotation.SuppressLint;
36
import android.app.Service;
47
import android.content.Intent;
58
import android.os.IBinder;
69

10+
import androidx.core.app.ServiceCompat;
711
import androidx.core.content.ContextCompat;
812

913
import com.squareup.otto.Subscribe;
@@ -21,13 +25,14 @@
2125
import de.stephanlindauer.criticalmaps.managers.LocationUpdateManager;
2226
import de.stephanlindauer.criticalmaps.provider.EventBus;
2327
import de.stephanlindauer.criticalmaps.utils.TrackingInfoNotificationBuilder;
28+
import timber.log.Timber;
2429

2530
public class ServerSyncService extends Service {
2631

2732
@SuppressWarnings("FieldCanBeLocal")
2833
private final int SERVER_SYNC_INTERVAL = 30 * 1000; // 30 sec
29-
3034
private Timer locationUploadTimer;
35+
private static boolean isRunning = false;
3136

3237
@Inject
3338
LocationUpdateManager locationUpdateManager;
@@ -46,18 +51,18 @@ public IBinder onBind(Intent intent) {
4651
return null;
4752
}
4853

54+
@SuppressLint("InlinedApi")
4955
@Override
5056
public void onCreate() {
51-
App.components().inject(this);
52-
53-
startForeground(TrackingInfoNotificationBuilder.NOTIFICATION_ID,
54-
TrackingInfoNotificationBuilder.getNotification(getApplication()));
55-
56-
locationUpdateManager.initializeAndStartListening();
57+
super.onCreate();
5758

59+
App.components().inject(this);
60+
ServiceCompat.startForeground(this, TrackingInfoNotificationBuilder.NOTIFICATION_ID,
61+
TrackingInfoNotificationBuilder.getNotification(getApplication()), FOREGROUND_SERVICE_TYPE_LOCATION);
5862
networkConnectivityChangeHandler.start();
59-
63+
locationUpdateManager.startListening();
6064
eventBus.register(this);
65+
isRunning = true;
6166
}
6267

6368
private void startLocationUploadTimer() {
@@ -85,6 +90,9 @@ public void onDestroy() {
8590
locationUpdateManager.handleShutdown();
8691
networkConnectivityChangeHandler.stop();
8792
stopLocationUploadTimer();
93+
isRunning = false;
94+
95+
super.onDestroy();
8896
}
8997

9098
@Override
@@ -104,6 +112,11 @@ public void handleNetworkConnectivityChanged(NetworkConnectivityChangedEvent e)
104112

105113
public static void startService() {
106114
App app = App.components().app();
115+
if (!LocationUpdateManager.checkPermission()) {
116+
Timber.d("ServerSyncService cannot be started without location and notification permission.");
117+
return;
118+
}
119+
107120
Intent syncServiceIntent = new Intent(app, ServerSyncService.class);
108121
ContextCompat.startForegroundService(app, syncServiceIntent);
109122
}
@@ -113,4 +126,8 @@ public static void stopService() {
113126
Intent syncServiceIntent = new Intent(app, ServerSyncService.class);
114127
app.stopService(syncServiceIntent);
115128
}
129+
130+
public static boolean isCurrentlyRunning() {
131+
return isRunning;
132+
}
116133
}

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,16 +39,16 @@
3939
the Mass, but remember that the app depends on as many people as possible sharing their
4040
location.\nSo please consider enabling location services in your device settings.</string>
4141
<string name="map_gps_permissions_permanently_denied_title">Location access is denied</string>
42-
<string name="map_gps_permissions_permanently_denied_text">You previously denied permission to access your location. Without location access you and other users won\'t be able to see your location on the map. You can still follow the Mass, but remember that the app depends on as many people as possible sharing their location.\nDo you want to grant the permission now in the app settings?</string>
43-
<string name="map_location_permissions_rationale_text">Location access is crucial for the app to work. It is required to view your location on the map, share you location with other users, write chat messages and upload images. Without it you can still follow the Mass, but remember that the app depends on as many people as possible sharing their location.\nDo you want to grant the permission now?</string>
42+
<string name="map_gps_permissions_permanently_denied_text">You previously denied permission to access your location and show notifications. Without location access you and other users won\'t be able to see your location on the map. Notifications are required to inform you that the app is still running in the background. You can still follow the Mass, but remember that the app depends on as many people as possible sharing their location.\nDo you want to grant the permission now in the app settings?</string>
43+
<string name="map_location_permissions_rationale_text">Location and notification access is crucial for the app to work. It is required to view your location on the map, share you location with other users, write chat messages and upload images. Notifications are required to inform you that the app is still running in the background. Without it you can still follow the Mass, but remember that the app depends on as many people as possible sharing their location.\nDo you want to grant the permission now?</string>
4444
<string name="map_oberserver_mode_info_heading">Observer mode is active</string>
4545
<string name="map_observer_mode_info_text">Already joined the mass?\nNice! Please disable observer mode now to help others see where you\'re going!</string>
4646

4747
<!-- notification -->
4848
<string name="notification_tracking_title">Critical Maps Tracking</string>
49-
<string name="notification_tracking_text">The App runs in the background and records your location data. Please close the App when you don\'t want to be tracked anymore.</string>
49+
<string name="notification_tracking_text">The App runs in the background and sends your location data. Please close the App when you don\'t want to be tracked anymore.</string>
5050
<string name="notification_channel_name">Tracking notification</string>
51-
<string name="notification_channel_description_max300chars">We do not recommend disabling the notification since you might inadvertently keep tracking turned on.</string>
51+
<string name="notification_channel_description_max300chars">It is not recommended to disable the notification since you might inadvertently keep tracking turned on.</string>
5252
<string name="notification_tracking_close">Close</string>
5353
<string name="notification_tracking_open">Open</string>
5454

0 commit comments

Comments
 (0)