From ed2693236c47b119a7604c0c93e8ead1a1e60bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20D=C3=B6rflinger?= Date: Wed, 26 Mar 2025 13:30:03 +0100 Subject: [PATCH] [ShareLocation] Added Settings to change values for location updates Now the requested frequency and the priority of location updates can be selected in a newly integrated SettingsActivity which can be extended for future use. --- .idea/sonarlint.xml | 6 + app/build.gradle | 3 +- app/src/main/AndroidManifest.xml | 15 ++- .../familyhelpers/HelperGridActivity.java | 20 +++ .../apps/familyhelpers/SettingsActivity.java | 39 ++++++ .../familyhelpers/workers/LocationHelper.java | 23 +++- .../ShareLocationBackgroundWorker.java | 117 +++++++++++++----- app/src/main/res/drawable/ic_settings.xml | 17 +++ app/src/main/res/layout/activity_main.xml | 17 ++- app/src/main/res/layout/settings_activity.xml | 9 ++ app/src/main/res/values/arrays.xml | 2 + app/src/main/res/values/strings.xml | 20 +++ app/src/main/res/xml/root_preferences.xml | 33 +++++ 13 files changed, 273 insertions(+), 48 deletions(-) create mode 100644 .idea/sonarlint.xml create mode 100644 app/src/main/java/com/aldo/apps/familyhelpers/SettingsActivity.java create mode 100644 app/src/main/res/drawable/ic_settings.xml create mode 100644 app/src/main/res/layout/settings_activity.xml create mode 100644 app/src/main/res/values/arrays.xml create mode 100644 app/src/main/res/xml/root_preferences.xml diff --git a/.idea/sonarlint.xml b/.idea/sonarlint.xml new file mode 100644 index 0000000..5b670f2 --- /dev/null +++ b/.idea/sonarlint.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 7822a96..4fca161 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -42,7 +42,8 @@ dependencies { implementation 'com.google.android.gms:play-services-location:21.3.0' // Glide - implementation 'com.github.bumptech.glide:glide:4.16.0' // Check for the latest version + implementation 'com.github.bumptech.glide:glide:4.16.0' + implementation 'androidx.preference:preference:1.2.0'// Check for the latest version annotationProcessor 'com.github.bumptech.glide:compiler:4.16.0' //CircleImageView diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9591bd0..df1d6c6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -20,10 +20,15 @@ android:supportsRtl="true" android:theme="@style/Theme.MyApplication" tools:targetApi="31"> + + @@ -33,23 +38,23 @@ - - + - - + diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java index 9c276bd..5174f5b 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/HelperGridActivity.java @@ -50,6 +50,11 @@ public class HelperGridActivity extends AppCompatActivity { */ private HelperGroupTile mShareLocationTile; + /** + * {@link HelperGroupTile} holding the settings. + */ + private HelperGroupTile mSettingsTile; + /** * Instance of the {@link DevicePolicyManagerHelper} to roll out device specific actions. */ @@ -110,6 +115,7 @@ public class HelperGridActivity extends AppCompatActivity { mDevicePolicyHelper = DevicePolicyManagerHelper.getInstance(this); initSleepTimer(); initShareLocation(); + initSettings(); } /** @@ -165,7 +171,21 @@ public class HelperGridActivity extends AppCompatActivity { }; mShareLocationTile.setLogo(R.drawable.ic_location_helper); mShareLocationTile.setTitle(R.string.title_share_location); + } + /** + * Helper method to initialize the settings tile. + */ + private void initSettings() { + mSettingsTile = new HelperGroupTile(findViewById(R.id.tile_settings)) { + @Override + public void launchHelper() { + final Intent intent = new Intent(HelperGridActivity.this, SettingsActivity.class); + startActivity(intent); + } + }; + mSettingsTile.setTitle(R.string.title_settings); + mSettingsTile.setLogo(R.drawable.ic_settings); } /** diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/SettingsActivity.java b/app/src/main/java/com/aldo/apps/familyhelpers/SettingsActivity.java new file mode 100644 index 0000000..6ef3481 --- /dev/null +++ b/app/src/main/java/com/aldo/apps/familyhelpers/SettingsActivity.java @@ -0,0 +1,39 @@ +package com.aldo.apps.familyhelpers; + +import android.os.Bundle; + +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.preference.PreferenceFragmentCompat; + +/** + * Simple Settings Activity for some app specific settings. + */ +public class SettingsActivity extends AppCompatActivity { + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.settings_activity); + if (savedInstanceState == null) { + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.settings, new SettingsFragment()) + .commit(); + } + final ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } + } + + /** + * The actual Settings fragment, containing the settings as defined in R.xml.root_preferences. + */ + public static class SettingsFragment extends PreferenceFragmentCompat { + @Override + public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { + setPreferencesFromResource(R.xml.root_preferences, rootKey); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java b/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java index 5383929..3da08a3 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/workers/LocationHelper.java @@ -39,7 +39,6 @@ import io.reactivex.rxjava3.subjects.BehaviorSubject; /** * Helper class to encapsulate all Location specific calls into one utility class. - * TODO: Change logic to make use of the FusedLocationProvider. */ public final class LocationHelper { @@ -67,6 +66,10 @@ public final class LocationHelper { */ private final FusedLocationProviderClient mFusedLocationProvider; + /** + * The {@link LocationCallback} to be invoked by the {@link FusedLocationProviderClient} as soon + * as a new {@link LocationResult} was received. + */ private final LocationCallback mLocationCallback = new LocationCallback() { @Override public void onLocationResult(@NonNull final LocationResult locationResult) { @@ -113,6 +116,11 @@ public final class LocationHelper { */ private boolean mIsSubscribed; + /** + * The currently selected frequency. + */ + private int mCurrentFrequency = -1; + /** * The current granularity used. */ @@ -219,8 +227,8 @@ public final class LocationHelper { */ public boolean startLocationUpdates(final Context context, final int minIntervalMillis, final int minDistance, final int priority) { - if (priority == mCurrentPriority) { - Log.d(TAG, "startLocationUpdates: Already subscribed with this granularity, no need to update"); + if (priority == mCurrentPriority && mCurrentFrequency == minIntervalMillis) { + Log.d(TAG, "startLocationUpdates: Already subscribed with this granularity and frequency, no need to update"); return true; } if (mIsSubscribed) { @@ -230,7 +238,8 @@ public final class LocationHelper { } if (ContextCompat.checkSelfPermission(context, ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { mCurrentPriority = priority; - final LocationRequest locationRequest = new LocationRequest.Builder(minIntervalMillis) + mCurrentFrequency = minIntervalMillis; + final LocationRequest locationRequest = new LocationRequest.Builder(mCurrentFrequency) .setGranularity(GRANULARITY_FINE) .setMinUpdateDistanceMeters(minDistance) .setPriority(mCurrentPriority) @@ -279,6 +288,12 @@ public final class LocationHelper { return mSharingStateSubject; } + /** + * Helper method to handle a received location by putting it to the database and inform listeners + * about the new location. + * + * @param location The received {@link Location}. + */ private void handleReceivedLocation(final Location location) { final LocationObject locationObject = LocationObject.fromLocation(location); Log.d(TAG, "onLocationChanged: Received update with " + locationObject); diff --git a/app/src/main/java/com/aldo/apps/familyhelpers/workers/ShareLocationBackgroundWorker.java b/app/src/main/java/com/aldo/apps/familyhelpers/workers/ShareLocationBackgroundWorker.java index 18ce427..58e4986 100644 --- a/app/src/main/java/com/aldo/apps/familyhelpers/workers/ShareLocationBackgroundWorker.java +++ b/app/src/main/java/com/aldo/apps/familyhelpers/workers/ShareLocationBackgroundWorker.java @@ -17,17 +17,21 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.util.Log; import androidx.annotation.Nullable; +import androidx.preference.PreferenceManager; + +import com.aldo.apps.familyhelpers.R; /** * Service implementation to keep location updates running also in the background. */ -public class ShareLocationBackgroundWorker extends Service { +public class ShareLocationBackgroundWorker extends Service implements SharedPreferences.OnSharedPreferenceChangeListener { /** * Tag for debugging purpose. @@ -49,6 +53,24 @@ public class ShareLocationBackgroundWorker extends Service { */ private Runnable mShareLocationRunnable; + /** + * Integer holding the frequency in which updates shall be received. Defaults to + * {@link com.aldo.apps.familyhelpers.utils.GlobalConstants#DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS} + */ + private int mSelectedFrequency; + + /** + * Flag indicating whether the subscription priority shall be automatically selected based on + * charging state. Defaults to true. + */ + private boolean mAutoPriority; + + /** + * Flag indicatin whether the non-automatically selected priority shall be high accuracy (== true) + * or balanced power consumption (== false). Defaults to false. + */ + private boolean mManualHighAccuracy; + @Override public void onCreate() { super.onCreate(); @@ -57,6 +79,11 @@ public class ShareLocationBackgroundWorker extends Service { batteryFilter.addAction(ACTION_POWER_CONNECTED); batteryFilter.addAction(ACTION_POWER_DISCONNECTED); registerReceiver(new BatteryStateReceiver(), batteryFilter); + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + mSelectedFrequency = preferences.getInt(getString(R.string.pref_key_share_location_freq), DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS); + mAutoPriority = preferences.getBoolean(getString(R.string.pref_key_share_location_auto_acc), true); + mManualHighAccuracy = preferences.getBoolean(getString(R.string.pref_key_share_location_man_acc), false); + preferences.registerOnSharedPreferenceChangeListener(this); } @Override @@ -71,20 +98,7 @@ public class ShareLocationBackgroundWorker extends Service { stopSelf(); return START_NOT_STICKY; } - final IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); - final Intent batteryStatus = registerReceiver(null, batteryFilter); - final int priority; - if (batteryStatus != null && batteryStatus.getIntExtra(EXTRA_STATUS, -1) == BATTERY_STATUS_CHARGING) { - Log.d(TAG, "onStartCommand: Phone is charging, go for high accuracy"); - priority = PRIORITY_HIGH_ACCURACY; - } else { - Log.d(TAG, "onStartCommand: Status unknown or not charging, balanced power consumption."); - priority = PRIORITY_BALANCED_POWER_ACCURACY; - } - mShareLocationRunnable = () -> - mLocationHelper.startLocationUpdates(ShareLocationBackgroundWorker.this, - DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS, - DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS, priority); + mShareLocationRunnable = this::triggerLocationSharing; mHandler.post(mShareLocationRunnable); startForeground(SLEEP_TIMER_NOTIFICATION_ID, mLocationHelper.buildNotification(this)); return super.onStartCommand(intent, flags, startId); @@ -103,6 +117,53 @@ public class ShareLocationBackgroundWorker extends Service { super.onDestroy(); } + /** + * Triggers the location updates based on the preferences set in the application. Will read out the + * currently selected switch values for whether the priority should be selected automatically or not + * and if not it will take the selected priority from the setting. If auto selection is enabled, the + * priority will be selected based on the current charging state. + */ + private void triggerLocationSharing() { + final int priority; + if (mAutoPriority) { + final IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); + final Intent batteryStatus = registerReceiver(null, batteryFilter); + if (batteryStatus != null && batteryStatus.getIntExtra(EXTRA_STATUS, -1) == BATTERY_STATUS_CHARGING) { + Log.d(TAG, "onStartCommand: Phone is charging, go for high accuracy"); + priority = PRIORITY_HIGH_ACCURACY; + } else { + Log.d(TAG, "onStartCommand: Status unknown or not charging, balanced power consumption."); + priority = PRIORITY_BALANCED_POWER_ACCURACY; + } + } else if (mManualHighAccuracy) { + priority = PRIORITY_HIGH_ACCURACY; + } else { + priority = PRIORITY_BALANCED_POWER_ACCURACY; + } + mLocationHelper.startLocationUpdates(ShareLocationBackgroundWorker.this, + mSelectedFrequency, + DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS, priority); + } + + @Override + public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, @Nullable final String key) { + if (!mLocationHelper.isCurrentlySharing()) { + Log.d(TAG, "onSharedPreferenceChanged: currently not sharing, nothing to do."); + return; + } + if (getString(R.string.pref_key_share_location_freq).equalsIgnoreCase(key)) { + mSelectedFrequency = sharedPreferences.getInt(key, DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS); + Log.d(TAG, "onSharedPreferenceChanged: Update Frequency changed to [" + mSelectedFrequency + "]"); + } else if (getString(R.string.pref_key_share_location_auto_acc).equalsIgnoreCase(key)) { + mAutoPriority = sharedPreferences.getBoolean(key, true); + Log.d(TAG, "onSharedPreferenceChanged: Auto Priority switch changed to [" + mAutoPriority + "]"); + } else if (getString(R.string.pref_key_share_location_man_acc).equalsIgnoreCase(key)) { + mManualHighAccuracy = sharedPreferences.getBoolean(key, false); + Log.d(TAG, "onSharedPreferenceChanged: HighPriority switch changed to [" + mManualHighAccuracy + "]"); + } + triggerLocationSharing(); + } + /** * Base {@link BroadcastReceiver} to listen for the battery state update, listening for whether * the device got plugged or unplugged. @@ -115,24 +176,14 @@ public class ShareLocationBackgroundWorker extends Service { Log.w(TAG, "onReceive: Empty intent, return..."); return; } - if (ACTION_POWER_CONNECTED.equals(intent.getAction())) { - Log.d(TAG, "onReceive: Phone was plugged in, changing to high accuracy mode"); - mLocationHelper.startLocationUpdates(context, - DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS, - DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS, - PRIORITY_HIGH_ACCURACY); - } else if (ACTION_POWER_DISCONNECTED.equals(intent.getAction())) { - Log.d(TAG, "onReceive: Phone was unplugged, changing to battery saving mode"); - mLocationHelper.startLocationUpdates(context, - DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS, - DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS, - PRIORITY_BALANCED_POWER_ACCURACY); - } else { - Log.d(TAG, "onReceive: Unknown action [" + intent.getAction() + "], go to low power mode"); - mLocationHelper.startLocationUpdates(context, - DEFAULT_MINIMUM_LOCATION_INTERVAL_MILLIS, - DEFAULT_MINIMUM_LOCATION_INTERVAL_METERS, - PRIORITY_LOW_POWER); + if (!mAutoPriority) { + Log.d(TAG, "onReceive: Automatic priority selection is not selected, no need to update."); + return; + } + final String action = intent.getAction(); + if (ACTION_POWER_CONNECTED.equals(action) || ACTION_POWER_DISCONNECTED.equals(action)) { + Log.d(TAG, "onReceive: Received [" + action + "], with automatic prio, retrigger"); + triggerLocationSharing(); } } } diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000..ddef9ee --- /dev/null +++ b/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,17 @@ + + + + diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index b636f8d..fb59a6b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -16,26 +16,33 @@ app:layout_constraintEnd_toEndOf="parent" tools:text="@string/welcome_message_test"/> - + android:columnCount="2"> + android:layout_columnWeight="1"/> + android:layout_columnWeight="1"/> - + + + \ No newline at end of file diff --git a/app/src/main/res/layout/settings_activity.xml b/app/src/main/res/layout/settings_activity.xml new file mode 100644 index 0000000..de6591a --- /dev/null +++ b/app/src/main/res/layout/settings_activity.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml new file mode 100644 index 0000000..e5f8fdc --- /dev/null +++ b/app/src/main/res/values/arrays.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e009b0b..ee87e48 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,5 +32,25 @@ This update was received at: %s Share your location In order to share your position also while in the background, please grant the always permission + SettingsActivity + + + Settings + + + Share Location + Location Update frequency + The minimum amount of seconds to wait between two location updates\nNote: The lower the value the higher the battery consumption + Automatic accuracy detection + Accuracy will be auto selected based on the current charging state + Select high accuracy vs. balanced power consumption below + Force high accuracy + High accuracy for location data, may drain battery faster + Lower accuracy for location data, but better battery consumption + + + location_update_freq + auto_accuracy + manual_accuracy \ No newline at end of file diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml new file mode 100644 index 0000000..fed5b95 --- /dev/null +++ b/app/src/main/res/xml/root_preferences.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + \ No newline at end of file