diff --git a/assets/text/faq/faq03-no-point-logged.md b/assets/text/faq/faq03-no-point-logged.md index e0d9eb68d..c703fc3ff 100644 --- a/assets/text/faq/faq03-no-point-logged.md +++ b/assets/text/faq/faq03-no-point-logged.md @@ -5,6 +5,10 @@ Sometimes your specified time interval will have passed, but no point was logged * On Android 6+ (Marshmallow), a new feature called *[doze mode](http://lifehacker.com/how-android-doze-works-and-how-to-tweak-it-to-save-you-1785921957)* was introduced, which severely restricts activity on the device after certain periods of inactivity. Be sure to grant the app permission to run in the background by disabling battery optimization. If you aren't sure, or if you've denied this permission you can [disable battery optimization for GPSLogger manually](https://android.stackexchange.com/a/129075/14996) which does not bypass doze mode but occasionally provides logging windows in which to work. It will not make a great difference though, doze mode is quite aggressive. +* Background permission or battery un-optimization wasn't granted to the app, and this can prevent the periodic alarm scheduling the app needs to get a new point. + +* This can also happen if the app isn't used for a long time, the OS starts to remove permissions from the app. + * Many vendors are also known to introduce their own _additional_ poorly written but aggressive battery optimization mechanisms. App developers don't have a way of detecting or working around these, and unfortunately the apps receive all the blame. You can see some partial workarounds on the [Don't Kill My App site](https://dontkillmyapp.com/?app=GPSLogger) * The GPS system will have attempted to find its location and given up after a while. This in turn means that Android OS will not have given a location to GPSLogger diff --git a/gpslogger/build.gradle b/gpslogger/build.gradle index 6a92746f5..feecd345e 100644 --- a/gpslogger/build.gradle +++ b/gpslogger/build.gradle @@ -37,16 +37,16 @@ repositories { } android { - compileSdkVersion 34 + compileSdk 35 defaultConfig { applicationId "com.mendhak.gpslogger" - minSdkVersion 16 - //noinspection ExpiredTargetSdkVersion - targetSdkVersion 30 + minSdkVersion 21 + + targetSdkVersion 35 compileSdk 34 - versionCode 131 - versionName "131-rc2" + versionCode 132 + versionName "132" // Used by AppAuth-Android manifestPlaceholders = [ @@ -121,8 +121,8 @@ android { dependencies { // implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation "androidx.activity:activity:1.8.2" - implementation "androidx.fragment:fragment:1.6.2" + implementation "androidx.activity:activity:1.9.2" + implementation "androidx.fragment:fragment:1.8.3" implementation "androidx.preference:preference:1.2.1" implementation "androidx.constraintlayout:constraintlayout:2.1.4" @@ -136,16 +136,12 @@ dependencies { //Debug Logging - implementation('org.slf4j:slf4j-api:1.7.30') - implementation('com.github.tony19:logback-android-classic:1.1.1-2'){ - exclude group: 'com.google.android', module: 'android' - } - - implementation('com.github.tony19:logback-android-core:1.1.1-2') + implementation 'org.slf4j:slf4j-api:2.0.7' + implementation 'com.github.tony19:logback-android:3.0.0' //Android lollipop/material features including the Toolbar - implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.appcompat:appcompat:1.7.0' //Cardviews @@ -164,7 +160,7 @@ dependencies { implementation 'com.github.dmytrodanylyk.android-process-button:library:1.0.4' //Android's WorkManager - implementation 'androidx.work:work-runtime:2.9.0' + implementation 'androidx.work:work-runtime:2.9.1' // We need to use Gson to help with WorkManager limitations implementation 'com.google.code.gson:gson:2.10.1' @@ -212,11 +208,11 @@ dependencies { implementation('org.apache.commons:commons-csv:1.9.0') //Libraries required for unit testing - testImplementation 'junit:junit:4.12' + testImplementation 'junit:junit:4.13.2' testImplementation 'org.mockito:mockito-core:3.10.0' testImplementation 'org.json:json:20180813' - testImplementation 'androidx.test:runner:1.5.2' - testImplementation 'androidx.test:rules:1.5.0' + testImplementation 'androidx.test:runner:1.6.2' + testImplementation 'androidx.test:rules:1.6.1' } diff --git a/gpslogger/src/main/AndroidManifest.xml b/gpslogger/src/main/AndroidManifest.xml index 775d9bfe5..2302b22b2 100644 --- a/gpslogger/src/main/AndroidManifest.xml +++ b/gpslogger/src/main/AndroidManifest.xml @@ -74,7 +74,8 @@ android:resource="@xml/shortcuts" /> - + @@ -94,7 +95,9 @@ + android:parentActivityName=".GpsMainActivity" + android:exported="true" + > @@ -105,7 +108,9 @@ android:name=".Faqtivity" android:label="@string/faq_screen_title" android:launchMode="singleTask" - android:parentActivityName=".GpsMainActivity" > + android:parentActivityName=".GpsMainActivity" + android:exported="true" + > @@ -114,6 +119,7 @@ @@ -143,6 +150,7 @@ @@ -150,6 +158,7 @@ @@ -159,6 +168,7 @@ @@ -168,13 +178,15 @@ - + - + @@ -185,6 +197,7 @@ - + diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java index 5e4550464..5d4513cab 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsLoggingService.java @@ -25,10 +25,14 @@ import android.content.Intent; import android.content.pm.ServiceInfo; import android.graphics.BitmapFactory; +import android.location.GnssStatus; import android.location.Location; import android.location.LocationManager; import android.os.*; + +import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.app.AlarmManagerCompat; import androidx.core.app.NotificationCompat; import androidx.core.app.TaskStackBuilder; import android.text.Html; @@ -70,6 +74,7 @@ public class GpsLoggingService extends Service { private LocationManager passiveLocationManager; private LocationManager towerLocationManager; private GeneralLocationListener gpsLocationListener; + private GnssStatus.Callback gnssStatusCallback; private GeneralLocationListener towerLocationListener; private GeneralLocationListener passiveLocationListener; private NmeaLocationListener nmeaLocationListener; @@ -166,7 +171,7 @@ public void onLowMemory() { LOG.error("Android is low on memory!"); Intent i = new Intent(this, GpsLoggingService.class); i.putExtra(IntentConstants.GET_NEXT_POINT, true); - PendingIntent pi = PendingIntent.getService(this, 0, i, 0); + PendingIntent pi = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_IMMUTABLE); nextPointAlarmManager.cancel(pi); nextPointAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + 300000, pi); super.onLowMemory(); @@ -319,7 +324,11 @@ public void setupAutoSendTimers() { alarmIntent = new Intent(this, AlarmReceiver.class); cancelAlarm(); - PendingIntent sender = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + flags |= PendingIntent.FLAG_MUTABLE; + } + PendingIntent sender = PendingIntent.getBroadcast(this, 0, alarmIntent, flags); AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerTime, sender); @@ -351,7 +360,8 @@ public void logOnce() { private void cancelAlarm() { if (alarmIntent != null) { AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); - PendingIntent sender = PendingIntent.getBroadcast(this, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent sender = PendingIntent.getBroadcast(this, 0, alarmIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE); am.cancel(sender); } } @@ -497,11 +507,11 @@ private Notification getNotification() { Intent stopLoggingIntent = new Intent(this, GpsLoggingService.class); stopLoggingIntent.setAction("NotificationButton_STOP"); stopLoggingIntent.putExtra(IntentConstants.IMMEDIATE_STOP, true); - PendingIntent piStop = PendingIntent.getService(this, 0, stopLoggingIntent, 0); + PendingIntent piStop = PendingIntent.getService(this, 0, stopLoggingIntent, PendingIntent.FLAG_IMMUTABLE); Intent annotateIntent = new Intent(this, NotificationAnnotationActivity.class); annotateIntent.setAction("com.mendhak.gpslogger.NOTIFICATION_BUTTON"); - PendingIntent piAnnotate = PendingIntent.getActivity(this,0, annotateIntent,0); + PendingIntent piAnnotate = PendingIntent.getActivity(this,0, annotateIntent, PendingIntent.FLAG_IMMUTABLE); // What happens when the notification item is clicked Intent contentIntent = new Intent(this, GpsMainActivity.class); @@ -509,7 +519,11 @@ private Notification getNotification() { TaskStackBuilder stackBuilder = TaskStackBuilder.create(this); stackBuilder.addNextIntent(contentIntent); - PendingIntent pending = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT); + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent pending = stackBuilder.getPendingIntent(0, flags); CharSequence contentTitle = getString(R.string.gpslogger_still_running); CharSequence contentText = getString(R.string.app_name); @@ -581,6 +595,7 @@ private void startPassiveManager() { } } + /** * Starts the location manager. There are two location managers - GPS and * Cell Tower. This code determines which manager to request updates from @@ -607,6 +622,32 @@ private void startGpsManager() { towerLocationListener = new GeneralLocationListener(this, "CELL"); } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + gnssStatusCallback = new GnssStatus.Callback() { + @Override + public void onStarted() { + super.onStarted(); + } + + @Override + public void onStopped() { + super.onStopped(); + } + + @Override + public void onFirstFix(int ttffMillis) { + super.onFirstFix(ttffMillis); + LOG.info("Time to first fix: {}ms", ttffMillis); + } + + @Override + public void onSatelliteStatusChanged(@NonNull GnssStatus status) { + super.onSatelliteStatusChanged(status); + setSatelliteInfo(status.getSatelliteCount()); + gpsLocationListener.satellitesUsedInFix = status.getSatelliteCount(); + } + }; + } gpsLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); @@ -618,7 +659,13 @@ private void startGpsManager() { LOG.info("Requesting GPS location updates"); // gps satellite based gpsLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, gpsLocationListener); - gpsLocationManager.addGpsStatusListener(gpsLocationListener); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + gpsLocationManager.registerGnssStatusCallback(gnssStatusCallback); + } + else { + gpsLocationManager.addGpsStatusListener(gpsLocationListener); + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (nmeaLocationListener == null){ @@ -715,6 +762,12 @@ private void stopGpsManager() { if (gpsLocationListener != null) { LOG.debug("Removing gpsLocationManager updates"); gpsLocationManager.removeUpdates(gpsLocationListener); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && gnssStatusCallback != null) { + gpsLocationManager.unregisterGnssStatusCallback(gnssStatusCallback); + } + else { gpsLocationManager.removeGpsStatusListener(gpsLocationListener); } @@ -1035,26 +1088,26 @@ protected void stopManagerAndResetAlarm() { private void stopAlarm() { Intent i = new Intent(this, GpsLoggingService.class); i.putExtra(IntentConstants.GET_NEXT_POINT, true); - PendingIntent pi = PendingIntent.getService(this, 0, i, 0); + PendingIntent pi = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_MUTABLE); nextPointAlarmManager.cancel(pi); } private void setAlarmForNextPoint() { - LOG.debug("Set alarm for " + preferenceHelper.getMinimumLoggingInterval() + " seconds"); - Intent i = new Intent(this, GpsLoggingService.class); i.putExtra(IntentConstants.GET_NEXT_POINT, true); - PendingIntent pi = PendingIntent.getService(this, 0, i, 0); + PendingIntent pi = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_MUTABLE); nextPointAlarmManager.cancel(pi); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - nextPointAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + preferenceHelper.getMinimumLoggingInterval() * 1000, pi); + + if(AlarmManagerCompat.canScheduleExactAlarms(nextPointAlarmManager)){ + LOG.debug("Setting exact alarm for {}s", preferenceHelper.getMinimumLoggingInterval()); + AlarmManagerCompat.setExactAndAllowWhileIdle(nextPointAlarmManager, AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + preferenceHelper.getMinimumLoggingInterval() * 1000L, pi); } else { - nextPointAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + preferenceHelper.getMinimumLoggingInterval() * 1000, pi); + LOG.debug("Setting inexact alarm for {}s (exact alarm disallowed/not available)", preferenceHelper.getMinimumLoggingInterval()); + AlarmManagerCompat.setAndAllowWhileIdle(nextPointAlarmManager, AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + preferenceHelper.getMinimumLoggingInterval() * 1000L, pi); } - } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsMainActivity.java b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsMainActivity.java index ad8a1a574..411f86ce6 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/GpsMainActivity.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/GpsMainActivity.java @@ -65,6 +65,7 @@ import androidx.appcompat.view.menu.ActionMenuItemView; import androidx.appcompat.widget.ActionMenuView; import androidx.appcompat.widget.Toolbar; +import androidx.core.app.ActivityCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; @@ -243,6 +244,7 @@ public void askUserForBasicPermissions() { .title(getString(R.string.gpslogger_permissions_rationale_title)) .msgHtml(getString(R.string.gpslogger_permissions_rationale_message_basic) + "
" + getString(R.string.gpslogger_permissions_rationale_message_location) + + (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ? "
" + getString(R.string.gpslogger_permissions_rationale_message_notification) : "") + "
" + getString(R.string.gpslogger_permissions_rationale_message_storage) + "
" + getString(R.string.gpslogger_permissions_rationale_message_location_background) + "
" + getString(R.string.gpslogger_permissions_rationale_message_battery_optimization) @@ -267,19 +269,7 @@ public boolean onResult(@NonNull String dialogTag, int which, @NonNull Bundle ex return true; case BUTTON_POSITIVE: LOG.debug("Beginning request for multiple permissions"); - ArrayList permissions = new ArrayList(); - permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION); - permissions.add(Manifest.permission.ACCESS_FINE_LOCATION); - permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE); - permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - permissions.add(Manifest.permission.POST_NOTIFICATIONS); - } - - if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) { - // Only on Android 10 (Q), the permission dialog can include an 'Allow all the time' - permissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION); - } + ArrayList permissions = new ArrayList(Systems.getListOfNecessaryPermissions(true)); basicPermissionsLauncher.launch(permissions.toArray(new String[0])); return true; } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/MainPreferenceActivity.java b/gpslogger/src/main/java/com/mendhak/gpslogger/MainPreferenceActivity.java index c41b5e0b1..498137e7b 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/MainPreferenceActivity.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/MainPreferenceActivity.java @@ -22,9 +22,13 @@ import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; import androidx.preference.PreferenceFragmentCompat; import android.view.MenuItem; +import android.view.ViewGroup; import com.mendhak.gpslogger.common.PreferenceHelper; import com.mendhak.gpslogger.common.Systems; import com.mendhak.gpslogger.common.slf4j.Logs; @@ -47,6 +51,25 @@ protected void onCreate(Bundle savedInstanceState) { Systems.setLocale(PreferenceHelper.getInstance().getUserSpecifiedLocale(),getBaseContext(),getResources()); setContentView(R.layout.activity_preferences); + ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.toolbar), (v, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()); + + // Apply the insets as a margin to the view so it doesn't overlap with status bar + ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams(); + mlp.leftMargin = insets.left; + mlp.bottomMargin = insets.bottom; + mlp.rightMargin = insets.right; + mlp.topMargin = insets.top; + v.setLayoutParams(mlp); + + // Alternatively set the padding on the view itself. +// v.setPadding(insets.left, 0, insets.right, insets.bottom); + + // Return CONSUMED if you don't want want the window insets to keep passing + // down to descendant views. + return WindowInsetsCompat.CONSUMED; + }); + Toolbar toolbar = (Toolbar)findViewById(R.id.toolbar); setSupportActionBar(toolbar); if(getSupportActionBar() != null){ diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/RejectionHandler.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/RejectionHandler.java index fced27d62..f772f0529 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/RejectionHandler.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/RejectionHandler.java @@ -30,9 +30,14 @@ public class RejectionHandler implements RejectedExecutionHandler { private static final Logger LOG = Logs.of(RejectionHandler.class); + private final String tag; + public RejectionHandler(String tag){ + this.tag = tag; + } + @Override public void rejectedExecution(Runnable runnable, ThreadPoolExecutor threadPoolExecutor) { - LOG.warn(SessionLogcatAppender.MARKER_INTERNAL, "Could not queue task, some points may not be logged."); + LOG.warn(SessionLogcatAppender.MARKER_INTERNAL, "Could not queue task for {}, some points may not be logged.", tag); } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java index 9bbb42f0b..13bb9b48e 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/Systems.java @@ -66,6 +66,7 @@ import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -83,7 +84,16 @@ public static String getAndroidId() { } public static BatteryInfo getBatteryInfo(Context context){ - Intent batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + Intent batteryIntent = null; + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + batteryIntent = ContextCompat.registerReceiver(context, null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED), + ContextCompat.RECEIVER_EXPORTED); + } + else { + batteryIntent = context.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + } + + int level = batteryIntent != null ? batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) : 0; int scale = batteryIntent != null ? batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, -1) : 0; @@ -205,21 +215,61 @@ public static void setLocale(String userSpecifiedLocale, Context baseContext, Re } } + /** + * Returns a list of the necessary permissions for the app. + * Permissions are also filtered by the SDK version + * @param forInteractivePermissionWorkflow - Whether this is a permission workflow, in which case 'access_background_location' doesn't usually get returned + */ + public static List getListOfNecessaryPermissions(boolean forInteractivePermissionWorkflow){ + List permissions = new ArrayList(); + permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION); + permissions.add(Manifest.permission.ACCESS_FINE_LOCATION); + + // In API 33, this storage permission has no effect. + // https://developer.android.com/reference/android/Manifest.permission#READ_EXTERNAL_STORAGE + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU){ + permissions.add(Manifest.permission.READ_EXTERNAL_STORAGE); + permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU){ + permissions.add(Manifest.permission.POST_NOTIFICATIONS); + } + + + if(forInteractivePermissionWorkflow){ + // Only on Android 10 (Q), the permission dialog can include an 'Allow all the time' + if(Build.VERSION.SDK_INT == Build.VERSION_CODES.Q){ + permissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION); + } + } + else { + // Only return when checking if a permission was granted or not, not for interactive workflow. + // Including this along with coarse/fine location permissions will get denied immediately. + // Instead you are expected to request coarse/fine location permissions first, then this *separately*. + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + permissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION); + } + } + + + return permissions; + } + /** * Whether the user has allowed the permissions absolutely required to run the app. * Currently this is location and file storage. */ public static boolean hasUserGrantedAllNecessaryPermissions(Context context){ - boolean granted = hasUserGrantedPermission(Manifest.permission.ACCESS_COARSE_LOCATION, context) - && hasUserGrantedPermission(Manifest.permission.ACCESS_FINE_LOCATION, context) - && hasUserGrantedPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, context) - && hasUserGrantedPermission(Manifest.permission.READ_EXTERNAL_STORAGE, context); - if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ - granted = granted && hasUserGrantedPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION, context); + List permissions = getListOfNecessaryPermissions(false); + for(String permission : permissions){ + if(!hasUserGrantedPermission(permission, context)){ + return false; + } } - return granted; + return true; } static boolean hasUserGrantedPermission(String permissionName, Context context){ @@ -301,7 +351,11 @@ public static void showErrorNotification(Context context, String message){ LOG.debug("Showing fatal notification"); Intent contentIntent = new Intent(context, GpsMainActivity.class); - PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT); + int flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, contentIntent, flags); NotificationCompat.Builder nfc = new NotificationCompat.Builder(context.getApplicationContext(), NotificationChannelNames.GPSLOGGER_ERRORS) .setSmallIcon(android.R.drawable.stat_sys_warning) diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/common/slf4j/SessionLogcatAppender.java b/gpslogger/src/main/java/com/mendhak/gpslogger/common/slf4j/SessionLogcatAppender.java index 10152dd0b..fbab8162a 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/common/slf4j/SessionLogcatAppender.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/common/slf4j/SessionLogcatAppender.java @@ -52,7 +52,7 @@ protected void append(ILoggingEvent eventObject) { if(eventObject.getLevel().toInt() < Level.INFO.toInt()){ return; } //Prevents certain entries from appearing in device logcat - if(eventObject.getMarker() == MARKER_INTERNAL){ return; } + if(eventObject.getMarkers() != null && eventObject.getMarkers().contains(MARKER_INTERNAL)){ return; } Statuses.add(eventObject); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/Files.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/Files.java index 5581027fe..ae2937d33 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/Files.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/Files.java @@ -99,13 +99,6 @@ public static String getMimeTypeFromFileName(String fileName) { } - public static void addToMediaDatabase(File file, String mimeType){ - - MediaScannerConnection.scanFile(AppSettings.getInstance(), - new String[]{file.getPath()}, - new String[]{mimeType}, - null); - } public static File[] fromFolder(File folder) { return fromFolder(folder, null); @@ -193,8 +186,6 @@ public static File createTestFile() throws IOException { initialOutput.write("This is a test file".getBytes()); initialOutput.flush(); initialOutput.close(); - - Files.addToMediaDatabase(testFile, "text/xml"); } return testFile; diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/csv/CSVFileLogger.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/csv/CSVFileLogger.java index 5d83ca92d..8d8084094 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/csv/CSVFileLogger.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/csv/CSVFileLogger.java @@ -165,8 +165,6 @@ public void annotate(String description, Location loc) throws Exception { ); } out.close(); - - Files.addToMediaDatabase(file, "text/csv"); } /** diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/geojson/GeoJSONLogger.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/geojson/GeoJSONLogger.java index 977ceaeed..47e2287da 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/geojson/GeoJSONLogger.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/geojson/GeoJSONLogger.java @@ -19,13 +19,14 @@ public class GeoJSONLogger implements FileLogger { final static Object lock = new Object(); - private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(10), new RejectionHandler()); private final File file; - protected final String name; + protected static final String name = "GeoJSON"; + private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, + TimeUnit.SECONDS, new LinkedBlockingQueue(10), new RejectionHandler(name)); + public GeoJSONLogger(File file, boolean addNewTrackSegment) { this.file = file; - name = "GeoJSON"; } @Override diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/gpx/Gpx10FileLogger.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/gpx/Gpx10FileLogger.java index 36af142d3..43d264915 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/gpx/Gpx10FileLogger.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/gpx/Gpx10FileLogger.java @@ -44,11 +44,12 @@ public class Gpx10FileLogger implements FileLogger { protected final static Object lock = new Object(); - private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, - new LinkedBlockingQueue(10), new RejectionHandler()); + private File gpxFile = null; private final boolean addNewTrackSegment; - protected final String name = "GPX"; + protected static final String name = "GPX"; + private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, + new LinkedBlockingQueue(10), new RejectionHandler(name)); public Gpx10FileLogger(File gpxFile, boolean addNewTrackSegment) { this.gpxFile = gpxFile; @@ -241,7 +242,6 @@ public void run() { raf.seek(startPosition); raf.write(trackPoint.getBytes()); raf.close(); - Files.addToMediaDatabase(gpxFile, "text/plain"); LOG.debug("Finished writing to GPX10 file"); } catch (Exception e) { diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/kml/Kml22FileLogger.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/kml/Kml22FileLogger.java index b9952a83c..c9aa645de 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/kml/Kml22FileLogger.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/kml/Kml22FileLogger.java @@ -41,11 +41,11 @@ public class Kml22FileLogger implements FileLogger { protected final static Object lock = new Object(); - private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, - new LinkedBlockingQueue(10), new RejectionHandler()); private final boolean addNewTrackSegment; private final File kmlFile; - protected final String name = "KML"; + protected static final String name = "KML"; + private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, + new LinkedBlockingQueue(10), new RejectionHandler(name)); public Kml22FileLogger(File kmlFile, boolean addNewTrackSegment) { @@ -221,7 +221,6 @@ public void run() { raf.seek(kmlFile.length() - 42); raf.write(coords.toString().getBytes()); raf.close(); - Files.addToMediaDatabase(kmlFile, "text/xml"); LOG.debug("Finished writing to KML22 File"); } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaFileLogger.java b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaFileLogger.java index 5653fcfcd..1dcbf5480 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaFileLogger.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/loggers/nmea/NmeaFileLogger.java @@ -44,8 +44,9 @@ public class NmeaFileLogger { protected final static Object lock = new Object(); private static final Logger LOG = Logs.of(NmeaFileLogger.class); String fileName; + protected static final String name = "NMEA"; private final static ThreadPoolExecutor EXECUTOR = new ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, - new LinkedBlockingQueue(10), new RejectionHandler()); + new LinkedBlockingQueue(30), new RejectionHandler(name)); private PreferenceHelper preferenceHelper = PreferenceHelper.getInstance(); private Session session = Session.getInstance(); @@ -98,7 +99,6 @@ public void run() { writer.write(nmeaSentence); writer.newLine(); writer.close(); - Files.addToMediaDatabase(gpxFile, "text/plain"); } catch (IOException e) { LOG.error("Error writing NMEA sentence", e); diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/display/GpsLogViewFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/display/GpsLogViewFragment.java index 7772a424f..04ae3850c 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/display/GpsLogViewFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/display/GpsLogViewFragment.java @@ -128,21 +128,21 @@ void showLogcatMessages(){ StringBuilder sb = new StringBuilder(); for(ILoggingEvent message : SessionLogcatAppender.Statuses){ - if(message.getMarker() == SessionLogcatAppender.MARKER_LOCATION){ - sb.append(getFormattedMessage(message.getMessage(), R.color.accentColorComplementary, message.getTimeStamp(), true)); + if(message.getMarkers() != null && message.getMarkers().contains(SessionLogcatAppender.MARKER_LOCATION)){ + sb.append(getFormattedMessage(message.getFormattedMessage(), R.color.accentColorComplementary, message.getTimeStamp(), true)); } else if(!chkLocationsOnly.isChecked()){ if(message.getLevel() == Level.ERROR) { - sb.append(getFormattedMessage(message.getMessage(), R.color.errorColor, message.getTimeStamp(), false)); + sb.append(getFormattedMessage(message.getFormattedMessage(), R.color.errorColor, message.getTimeStamp(), false)); } else if(message.getLevel() == Level.WARN){ - sb.append(getFormattedMessage(message.getMessage(), R.color.warningColor, message.getTimeStamp(), false)); + sb.append(getFormattedMessage(message.getFormattedMessage(), R.color.warningColor, message.getTimeStamp(), false)); } else { - sb.append(getFormattedMessage(message.getMessage(), R.color.secondaryColorText, message.getTimeStamp(), false)); + sb.append(getFormattedMessage(message.getFormattedMessage(), R.color.secondaryColorText, message.getTimeStamp(), false)); } } diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/GoogleDriveSettingsFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/GoogleDriveSettingsFragment.java index d15707d3c..a927c86c4 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/GoogleDriveSettingsFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/GoogleDriveSettingsFragment.java @@ -178,7 +178,7 @@ public boolean onPreferenceClick(Preference preference) { AuthorizationRequest authRequest = requestBuilder.build(); Intent authIntent = authorizationService.getAuthorizationRequestIntent(authRequest); googleDriveAuthenticationWorkflow.launch(new IntentSenderRequest.Builder( - PendingIntent.getActivity(getActivity(), 0, authIntent, 0)) + PendingIntent.getActivity(getActivity(), 0, authIntent, PendingIntent.FLAG_IMMUTABLE)) .setFillInIntent(authIntent) .build()); diff --git a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OSMAuthorizationFragment.java b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OSMAuthorizationFragment.java index 7b2b28960..a93a08da7 100644 --- a/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OSMAuthorizationFragment.java +++ b/gpslogger/src/main/java/com/mendhak/gpslogger/ui/fragments/settings/OSMAuthorizationFragment.java @@ -204,7 +204,7 @@ public boolean onPreferenceClick(Preference preference) { AuthorizationRequest authRequest = requestBuilder.build(); Intent authIntent = authorizationService.getAuthorizationRequestIntent(authRequest); openStreetMapAuthenticationWorkflow.launch(new IntentSenderRequest.Builder( - PendingIntent.getActivity(getActivity(), 53, authIntent, 0)) + PendingIntent.getActivity(getActivity(), 53, authIntent, PendingIntent.FLAG_IMMUTABLE)) .setFillInIntent(authIntent) .build()); diff --git a/gpslogger/src/main/res/layout/toolbar.xml b/gpslogger/src/main/res/layout/toolbar.xml index b1619ad7e..e07c69e87 100644 --- a/gpslogger/src/main/res/layout/toolbar.xml +++ b/gpslogger/src/main/res/layout/toolbar.xml @@ -4,10 +4,9 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/toolbar" - android:layout_height="55dp" + android:layout_height="wrap_content" android:layout_width="match_parent" android:minHeight="?attr/actionBarSize" android:background="?attr/colorPrimary" - android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" - app:contentInsetStart="72dp"> + android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"> \ No newline at end of file diff --git a/gpslogger/src/main/res/values/strings.xml b/gpslogger/src/main/res/values/strings.xml index ae8eca117..a19da8f39 100644 --- a/gpslogger/src/main/res/values/strings.xml +++ b/gpslogger/src/main/res/values/strings.xml @@ -445,6 +445,7 @@ Permissions required Device location - This permission allows reading location data from cell towers and the device GPS hardware.]]> + Notifications - Allow showing a notification, this helps with keeping the logging service alive. ]]> Photos, media, files - This permission allows writing log files and test files to storage, and reading from a list of files for uploading or sharing. This app will not access your photos or media.]]> Allow all the time - Allow this app to receive location updates when running in the background. ]]> Ignore battery optimization - Allow this app to run in the background by reducing battery optimization. ]]> diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c02..2c3521197 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index da1db5f04..09523c0e5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0c8..f5feea6d6 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,69 +15,104 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +122,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +133,120 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index ac1b06f93..9b42019c7 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,8 +13,10 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +27,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -75,13 +78,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal