diff --git a/.aiexclude b/.aiexclude
new file mode 100644
index 00000000..61ab8a69
--- /dev/null
+++ b/.aiexclude
@@ -0,0 +1 @@
+keystore.properties
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml
index 1f22cf30..0cb0a3c6 100644
--- a/.github/workflows/android.yml
+++ b/.github/workflows/android.yml
@@ -14,13 +14,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@v3
+ uses: actions/checkout@v5
- name: Gradle Wrapper Validation
- uses: gradle/wrapper-validation-action@v1
+ uses: gradle/actions/wrapper-validation@v3
- name: Install JDK
- uses: actions/setup-java@v2
+ uses: actions/setup-java@v5
with:
distribution: 'zulu'
java-version: 17
diff --git a/app/build.gradle b/app/build.gradle
index c2e81feb..01b3064c 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -21,12 +21,12 @@ android {
}
}
- compileSdk 33
+ compileSdk 35
defaultConfig {
applicationId "de.stephanlindauer.criticalmaps"
minSdkVersion 16
- targetSdkVersion 33
+ targetSdkVersion 35
versionCode 48
versionName "2.9.0"
vectorDrawables.useSupportLibrary = true
@@ -39,7 +39,7 @@ android {
debug {
applicationIdSuffix ".debug"
pseudoLocalesEnabled true
- minifyEnabled true
+ minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
@@ -64,6 +64,7 @@ android {
buildFeatures {
viewBinding = true
+ buildConfig true
}
lint {
@@ -77,10 +78,11 @@ dependencies {
implementation 'com.squareup:otto:1.3.8'
implementation 'org.osmdroid:osmdroid-android:6.1.8'
implementation 'com.squareup.picasso:picasso:2.8'
+ implementation 'androidx.core:core:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
- implementation 'androidx.annotation:annotation:1.6.0'
+ implementation 'androidx.annotation:annotation:1.9.1'
implementation 'com.google.android.material:material:1.9.0'
- implementation 'androidx.exifinterface:exifinterface:1.3.6'
+ implementation 'androidx.exifinterface:exifinterface:1.3.7'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'com.jakewharton.timber:timber:5.0.1'
@@ -92,7 +94,7 @@ dependencies {
implementation "com.google.dagger:dagger:$dagger_version"
annotationProcessor "com.google.dagger:dagger-compiler:$dagger_version"
- debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
+ debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14'
errorprone("com.google.errorprone:error_prone_core:2.18.0")
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 954e986a..66de55b5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -11,6 +11,10 @@
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
+
+
{
@@ -321,12 +325,6 @@ public void onResume() {
sharedPreferences.registerOnSharedPreferenceChangeListener(
observerModeOnSharedPreferenceChangeListener);
- if (locationUpdateManager.checkPermission()) {
- locationUpdateManager.startListening();
- } else {
- zoomToLocation(defaultGeoPoint, NO_GPS_PERMISSION_ZOOM_LEVEL);
- }
-
startGetLocationTimer();
}
@@ -505,7 +503,7 @@ public void run() {
getLocationHandler.get().execute();
}
};
- timerGetLocation.scheduleAtFixedRate(timerTaskPullServer, 0, SERVER_SYNC_INTERVAL);
+ timerGetLocation.schedule(timerTaskPullServer, 0, SERVER_SYNC_INTERVAL);
}
private void stopGetLocationTimer() {
diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/handler/PermissionCheckHandler.java b/app/src/main/java/de/stephanlindauer/criticalmaps/handler/PermissionCheckHandler.java
index 536aada7..41352016 100644
--- a/app/src/main/java/de/stephanlindauer/criticalmaps/handler/PermissionCheckHandler.java
+++ b/app/src/main/java/de/stephanlindauer/criticalmaps/handler/PermissionCheckHandler.java
@@ -42,7 +42,7 @@ public void requestPermissionsWithRationaleIfNeeded(PermissionRequest permission
activePermissionRequest = permissionRequest;
// short-circuit here if already granted
- if (checkPermissionsGranted(permissionRequest.getPermissions())) {
+ if (checkAllPermissionsGranted(permissionRequest.getPermissions())) {
activePermissionRequest.getOnGrantedCallback().run();
activePermissionRequest = null;
return;
@@ -91,7 +91,8 @@ public boolean handlePermissionRequestCallback(int requestCode, int[] grantResul
return false;
}
- boolean allPermissionsGranted = true;
+ // Note: Can be an empty array that should be treated as cancellation.
+ boolean allPermissionsGranted = grantResults.length > 0;
for (int result : grantResults) {
allPermissionsGranted = allPermissionsGranted &&
result == PackageManager.PERMISSION_GRANTED;
@@ -120,7 +121,7 @@ public boolean handlePermissionRequestCallback(int requestCode, int[] grantResul
return true;
}
- public static boolean checkPermissionsGranted(String[] permissions) {
+ public static boolean checkAllPermissionsGranted(String[] permissions) {
boolean permissionsGranted = true;
for (String permission : permissions) {
permissionsGranted = permissionsGranted && (PackageManager.PERMISSION_GRANTED ==
diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/managers/LocationUpdateManager.java b/app/src/main/java/de/stephanlindauer/criticalmaps/managers/LocationUpdateManager.java
index 75bebe09..7b2b8837 100644
--- a/app/src/main/java/de/stephanlindauer/criticalmaps/managers/LocationUpdateManager.java
+++ b/app/src/main/java/de/stephanlindauer/criticalmaps/managers/LocationUpdateManager.java
@@ -3,11 +3,14 @@
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
+import androidx.core.content.ContextCompat;
+
import com.squareup.otto.Produce;
import org.osmdroid.util.GeoPoint;
@@ -42,9 +45,12 @@ public class LocationUpdateManager {
private final String[] USED_PROVIDERS = new String[]{
LocationManager.GPS_PROVIDER,
LocationManager.NETWORK_PROVIDER};
+
+ @SuppressLint("InlinedApi")
private final String[] PERMISSIONS = {
Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.ACCESS_COARSE_LOCATION};
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ Manifest.permission.POST_NOTIFICATIONS};
private final LocationManager locationManager;
private Location lastPublishedLocation;
@@ -143,7 +149,7 @@ public boolean isUpdating() {
return isUpdating;
}
- public void initializeAndStartListening() {
+ public void initialize() {
boolean noProviderExists = !checkIfAtLeastOneProviderExits();
boolean noPermission = !checkPermission();
@@ -165,8 +171,6 @@ public void initializeAndStartListening() {
if (noPermission) {
return;
}
-
- startListening();
}
public void startListening() {
@@ -203,8 +207,12 @@ private void requestLocationUpdatesIfProviderExists(String provider) {
}
}
- public boolean checkPermission() {
- return PermissionCheckHandler.checkPermissionsGranted(PERMISSIONS);
+ @SuppressLint("InlinedApi")
+ public static boolean checkPermission() {
+ App app = App.components().app();
+ return (ContextCompat.checkSelfPermission(app, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
+ || ContextCompat.checkSelfPermission(app, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)
+ && ContextCompat.checkSelfPermission(app, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED;
}
public void requestPermission() {
diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.java
index 3b250204..89eab988 100644
--- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.java
+++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/ChatModel.java
@@ -11,7 +11,6 @@
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.Comparator;
import java.util.Date;
import java.util.List;
diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.java b/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.java
index 31cd96c8..b45a3154 100644
--- a/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.java
+++ b/app/src/main/java/de/stephanlindauer/criticalmaps/model/OtherUsersLocationModel.java
@@ -6,10 +6,8 @@
import org.osmdroid.util.GeoPoint;
import java.util.ArrayList;
-import java.util.Iterator;
import javax.inject.Inject;
-import javax.inject.Provider;
import javax.inject.Singleton;
@Singleton
diff --git a/app/src/main/java/de/stephanlindauer/criticalmaps/service/ServerSyncService.java b/app/src/main/java/de/stephanlindauer/criticalmaps/service/ServerSyncService.java
index aab35951..3bd1b866 100644
--- a/app/src/main/java/de/stephanlindauer/criticalmaps/service/ServerSyncService.java
+++ b/app/src/main/java/de/stephanlindauer/criticalmaps/service/ServerSyncService.java
@@ -1,9 +1,13 @@
package de.stephanlindauer.criticalmaps.service;
+import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION;
+
+import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
+import androidx.core.app.ServiceCompat;
import androidx.core.content.ContextCompat;
import com.squareup.otto.Subscribe;
@@ -21,13 +25,14 @@
import de.stephanlindauer.criticalmaps.managers.LocationUpdateManager;
import de.stephanlindauer.criticalmaps.provider.EventBus;
import de.stephanlindauer.criticalmaps.utils.TrackingInfoNotificationBuilder;
+import timber.log.Timber;
public class ServerSyncService extends Service {
@SuppressWarnings("FieldCanBeLocal")
private final int SERVER_SYNC_INTERVAL = 30 * 1000; // 30 sec
-
- private Timer timerPullServer;
+ private Timer locationUploadTimer;
+ private static boolean isRunning = false;
@Inject
LocationUpdateManager locationUpdateManager;
@@ -46,22 +51,22 @@ public IBinder onBind(Intent intent) {
return null;
}
+ @SuppressLint("InlinedApi")
@Override
public void onCreate() {
- App.components().inject(this);
-
- startForeground(TrackingInfoNotificationBuilder.NOTIFICATION_ID,
- TrackingInfoNotificationBuilder.getNotification(getApplication()));
-
- locationUpdateManager.initializeAndStartListening();
+ super.onCreate();
+ App.components().inject(this);
+ ServiceCompat.startForeground(this, TrackingInfoNotificationBuilder.NOTIFICATION_ID,
+ TrackingInfoNotificationBuilder.getNotification(getApplication()), FOREGROUND_SERVICE_TYPE_LOCATION);
networkConnectivityChangeHandler.start();
-
+ locationUpdateManager.startListening();
eventBus.register(this);
+ isRunning = true;
}
- private void startPullServerTimer() {
- timerPullServer = new Timer();
+ private void startLocationUploadTimer() {
+ locationUploadTimer = new Timer();
TimerTask timerTaskPullServer = new TimerTask() {
@Override
@@ -69,13 +74,13 @@ public void run() {
putLocationHandler.get().execute();
}
};
- timerPullServer.scheduleAtFixedRate(timerTaskPullServer, 0, SERVER_SYNC_INTERVAL);
+ locationUploadTimer.schedule(timerTaskPullServer, 0, SERVER_SYNC_INTERVAL);
}
- private void stopPullServerTimer() {
- if (timerPullServer != null) {
- timerPullServer.cancel();
- timerPullServer = null;
+ private void stopLocationUploadTimer() {
+ if (locationUploadTimer != null) {
+ locationUploadTimer.cancel();
+ locationUploadTimer = null;
}
}
@@ -84,7 +89,10 @@ public void onDestroy() {
eventBus.unregister(this);
locationUpdateManager.handleShutdown();
networkConnectivityChangeHandler.stop();
- stopPullServerTimer();
+ stopLocationUploadTimer();
+ isRunning = false;
+
+ super.onDestroy();
}
@Override
@@ -95,15 +103,20 @@ public void onTaskRemoved(Intent rootIntent) {
@Subscribe
public void handleNetworkConnectivityChanged(NetworkConnectivityChangedEvent e) {
- if (e.isConnected && timerPullServer == null) {
- startPullServerTimer();
+ if (e.isConnected && locationUploadTimer == null) {
+ startLocationUploadTimer();
} else {
- stopPullServerTimer();
+ stopLocationUploadTimer();
}
}
public static void startService() {
App app = App.components().app();
+ if (!LocationUpdateManager.checkPermission()) {
+ Timber.d("ServerSyncService cannot be started without location and notification permission.");
+ return;
+ }
+
Intent syncServiceIntent = new Intent(app, ServerSyncService.class);
ContextCompat.startForegroundService(app, syncServiceIntent);
}
@@ -113,4 +126,8 @@ public static void stopService() {
Intent syncServiceIntent = new Intent(app, ServerSyncService.class);
app.stopService(syncServiceIntent);
}
+
+ public static boolean isCurrentlyRunning() {
+ return isRunning;
+ }
}
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index 2911c9e6..2c1a6941 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -18,7 +18,11 @@
+ android:orientation="vertical"
+ android:paddingStart="16dp"
+ android:paddingLeft="16dp"
+ android:paddingEnd="16dp"
+ android:paddingRight="16dp">
+ android:minHeight="?android:attr/listPreferredItemHeight">
@@ -104,10 +98,6 @@
android:id="@+id/settings_cache_storagespacegraph"
android:layout_width="match_parent"
android:layout_height="16dp"
- android:layout_marginStart="16dp"
- android:layout_marginLeft="16dp"
- android:paddingEnd="?android:attr/scrollbarSize"
- android:paddingRight="?android:attr/scrollbarSize"
app:freeSpaceColor="@color/settings_free_space"
app:tilecacheColor="@color/settings_cache_space"
app:usedSpaceColor="@color/settings_used_space" />
@@ -115,12 +105,8 @@
+ android:orientation="horizontal">
-
-
+ android:minHeight="?android:attr/listPreferredItemHeight">
@@ -227,13 +201,6 @@
android:textColor="?android:attr/textColorSecondary"
tools:text="@string/storage_name_internal" />
-
-
@@ -242,11 +209,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:gravity="start"
- android:paddingStart="16dp"
- android:paddingLeft="16dp"
android:paddingTop="16dp"
- android:paddingEnd="16dp"
- android:paddingRight="16dp"
android:text="@string/settings_screen_header"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
@@ -257,24 +220,12 @@
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="?android:attr/scrollbarSize"
- android:paddingRight="?android:attr/scrollbarSize">
-
-
+ android:minHeight="?android:attr/listPreferredItemHeight">
@@ -326,24 +277,12 @@
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="?android:attr/scrollbarSize"
- android:paddingRight="?android:attr/scrollbarSize">
-
-
+ android:minHeight="?android:attr/listPreferredItemHeight">
@@ -391,11 +330,7 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:gravity="start"
- android:paddingStart="16dp"
- android:paddingLeft="16dp"
android:paddingTop="16dp"
- android:paddingEnd="16dp"
- android:paddingRight="16dp"
android:text="@string/settings_map_header"
android:textAlignment="viewStart"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
@@ -406,24 +341,12 @@
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="?android:attr/scrollbarSize"
- android:paddingRight="?android:attr/scrollbarSize">
-
-
+ android:minHeight="?android:attr/listPreferredItemHeight">
@@ -471,24 +394,12 @@
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="?android:attr/scrollbarSize"
- android:paddingRight="?android:attr/scrollbarSize">
-
-
+ android:minHeight="?android:attr/listPreferredItemHeight">
@@ -536,24 +447,12 @@
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:paddingEnd="?android:attr/scrollbarSize"
- android:paddingRight="?android:attr/scrollbarSize">
-
-
+ android:minHeight="?android:attr/listPreferredItemHeight">
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index fe2371f2..4d8e7942 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -39,16 +39,16 @@
the Mass, but remember that the app depends on as many people as possible sharing their
location.\nSo please consider enabling location services in your device settings.
Location access is denied
- 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?
- 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?
+ 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?
+ 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?
Observer mode is active
Already joined the mass?\nNice! Please disable observer mode now to help others see where you\'re going!
Critical Maps Tracking
- The App runs in the background and records your location data. Please close the App when you don\'t want to be tracked anymore.
+ The App runs in the background and sends your location data. Please close the App when you don\'t want to be tracked anymore.
Tracking notification
- We do not recommend disabling the notification since you might inadvertently keep tracking turned on.
+ It is not recommended to disable the notification since you might inadvertently keep tracking turned on.
Close
Open
diff --git a/build.gradle b/build.gradle
index 253e0d50..295774b1 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- dagger_version = "2.47"
+ dagger_version = "2.52"
}
repositories {
@@ -9,7 +9,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:8.1.0'
+ classpath 'com.android.tools.build:gradle:8.11.2'
classpath 'com.github.bjoernq:unmockplugin:0.7.9'
classpath 'net.ltgt.gradle:gradle-errorprone-plugin:3.1.0'
}
diff --git a/gradle.properties b/gradle.properties
index 3cb77d49..0d6a5ff1 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -11,7 +11,6 @@
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
-android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index dcda6fb5..8c8d5c18 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip