Skip to content

Commit

Permalink
Correctly process tracking state changes
Browse files Browse the repository at this point in the history
  • Loading branch information
andreynovikov committed Feb 2, 2024
1 parent 9f2cf03 commit dc023c7
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 43 deletions.
1 change: 1 addition & 0 deletions app/src/main/java/mobi/maptrek/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public class Configuration {
public static final long ADVICE_MAP_SETTINGS = 0x0000000000000020L;
public static final long ADVICE_ADDING_PLACE = 0x0000000000000040L;
public static final long ADVICE_RECORD_TRACK = 0x0000000000000080L;
/** @noinspection unused*/
public static final long ADVICE_RECORDED_TRACKS = 0x0000000000000100L;
public static final long ADVICE_VIEW_DATA_ITEM = 0x0000000000000200L;
public static final long ADVICE_SWITCH_COORDINATES_FORMAT = 0x0000000000000400L;
Expand Down
33 changes: 11 additions & 22 deletions app/src/main/java/mobi/maptrek/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -909,6 +909,7 @@ protected void onStart() {
ContextCompat.registerReceiver(this, mBroadcastReceiver, new IntentFilter(MapWorker.BROADCAST_MAP_ADDED), ContextCompat.RECEIVER_NOT_EXPORTED);
ContextCompat.registerReceiver(this, mBroadcastReceiver, new IntentFilter(MapWorker.BROADCAST_MAP_REMOVED), ContextCompat.RECEIVER_NOT_EXPORTED);
ContextCompat.registerReceiver(this, mBroadcastReceiver, new IntentFilter(MapWorker.BROADCAST_MAP_FAILED), ContextCompat.RECEIVER_NOT_EXPORTED);
ContextCompat.registerReceiver(this, mBroadcastReceiver, new IntentFilter(BaseLocationService.BROADCAST_TRACK_STATE), ContextCompat.RECEIVER_NOT_EXPORTED);
ContextCompat.registerReceiver(this, mBroadcastReceiver, new IntentFilter(BaseLocationService.BROADCAST_TRACK_SAVE), ContextCompat.RECEIVER_NOT_EXPORTED);
ContextCompat.registerReceiver(this, mBroadcastReceiver, new IntentFilter(NavigationService.BROADCAST_NAVIGATION_STATUS), ContextCompat.RECEIVER_NOT_EXPORTED);
ContextCompat.registerReceiver(this, mBroadcastReceiver, new IntentFilter(NavigationService.BROADCAST_NAVIGATION_STATE), ContextCompat.RECEIVER_NOT_EXPORTED);
Expand Down Expand Up @@ -939,6 +940,8 @@ protected void onResume() {
askForPermission(PERMISSIONS_REQUEST_FINE_LOCATION);

TRACKING_STATE savedState = TRACKING_STATE.values()[Configuration.getTrackingState()];
logger.error("Saved tracking state: {}", savedState);
logger.error("Current tracking state: {}", trackViewModel.trackingState.getValue());
if (savedState != trackViewModel.trackingState.getValue()) {
if (savedState == TRACKING_STATE.TRACKING)
enableTracking();
Expand All @@ -951,7 +954,7 @@ else if (savedState == TRACKING_STATE.PAUSED)
int recordColor = trackingState == TRACKING_STATE.TRACKING ? mColorAccent : mColorActionIcon;
mViews.tracksButton.getDrawable().setTint(recordColor);

if (trackingState == TRACKING_STATE.TRACKING || trackingState == TRACKING_STATE.PAUSED && mCurrentTrackLayer == null) {
if ((trackingState == TRACKING_STATE.TRACKING || trackingState == TRACKING_STATE.PAUSED) && mCurrentTrackLayer == null) {
mCurrentTrackLayer = new CurrentTrackLayer(mMap, getApplicationContext(), this);
mMap.layers().add(mCurrentTrackLayer, MAP_DATA);
mMap.updateMap(true);
Expand All @@ -965,7 +968,6 @@ else if (savedState == TRACKING_STATE.PAUSED)
trackViewModel.currentTrack.setValue(null);
mMap.updateMap(true);
}
trackViewModel.trackingCommand.setValue(trackingState); // reset command
});
trackViewModel.trackingCommand.observe(this, trackingCommand -> {
TRACKING_STATE trackingState = trackViewModel.trackingState.getValue();
Expand Down Expand Up @@ -1748,6 +1750,7 @@ private void onMapsLongClicked() {
menu.findItem(R.id.actionAutoTilt).setChecked(mAutoTilt != -1f);
});
showExtendPanel(PANEL_STATE.MAPS, "mapMenu", fragment);
Configuration.setAdviceState(Configuration.ADVICE_MAP_SETTINGS);
}

private void onMoreClicked() {
Expand Down Expand Up @@ -2039,6 +2042,7 @@ public void setHighlightedType(int type) {
}

private void enableTracking() {
logger.error("enableTracking");
Intent intent = new Intent(getApplicationContext(), LocationService.class).setAction(BaseLocationService.ENABLE_TRACK);
if (Build.VERSION.SDK_INT >= 26)
startForegroundService(intent);
Expand Down Expand Up @@ -3754,14 +3758,6 @@ public void onFragmentDetached(@NonNull FragmentManager fm, @NonNull Fragment f)
mViews.mapsButton,
false
);
else if (cls == TrackProperties.class)
HelperUtils.showTargetedAdvice(
MainActivity.this,
Configuration.ADVICE_RECORDED_TRACKS,
R.string.advice_recorded_tracks,
mViews.tracksButton,
false
);
}
};

Expand Down Expand Up @@ -4069,20 +4065,18 @@ public void onReceive(Context context, Intent intent) {
String title = extras != null ? extras.getString(MapWorker.EXTRA_TITLE) : getString(R.string.map);
HelperUtils.showError(getString(R.string.msgMapDownloadFailed, title), mViews.coordinatorLayout);
}
if (BaseLocationService.BROADCAST_TRACK_STATE.equals(action)) {
int stateOrdinal = intent.getIntExtra("state", TRACKING_STATE.DISABLED.ordinal());
TRACKING_STATE state = TRACKING_STATE.values()[stateOrdinal];
trackViewModel.trackingState.setValue(state);
}
if (BaseLocationService.BROADCAST_TRACK_SAVE.equals(action)) {
final Bundle extras = intent.getExtras();
boolean saved = extras != null && extras.getBoolean("saved");
if (saved) {
logger.debug("Track saved: {}", extras.getString("path"));
Snackbar.make(mViews.coordinatorLayout, R.string.msgTrackSaved, Snackbar.LENGTH_LONG)
.setAction(R.string.actionCustomize, view -> onTrackProperties(extras.getString("path")))
.addCallback(new BaseTransientBottomBar.BaseCallback<Snackbar>(){
@Override
public void onDismissed(Snackbar snackbar, @DismissEvent int event) {
if (event != DISMISS_EVENT_ACTION)
HelperUtils.showTargetedAdvice(MainActivity.this, Configuration.ADVICE_RECORDED_TRACKS, R.string.advice_recorded_tracks, mViews.tracksButton, false);
}
})
.setAnchorView(mViews.actionPanel)
.show();
return;
Expand Down Expand Up @@ -4512,11 +4506,6 @@ public void onConfigurationChanged(Configuration.ChangedEvent event) {
mHillshadeLayer.setBitmapAlpha(1 - transparency * 0.01f);
break;
}
case Configuration.PREF_TRACKING_STATE: {
int state = Configuration.getTrackingState();
trackViewModel.trackingState.setValue(TRACKING_STATE.values()[state]);
break;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public abstract class BaseLocationService extends Service {
* state.
*/
public static final String DISABLE_BACKGROUND_LOCATIONS = "mobi.maptrek.location.disableBackgroundLocations";
/**
* Broadcast sent when track recording state changes
*/
public static final String BROADCAST_TRACK_STATE = "mobi.maptrek.location.TrackState";
/**
* Broadcast sent when track is about to be saved (or not)
*/
Expand Down
51 changes: 31 additions & 20 deletions app/src/main/java/mobi/maptrek/location/LocationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,24 +190,29 @@ public int onStartCommand(Intent intent, int flags, int startId) {
return START_NOT_STICKY;

String action = intent.getAction();
logger.debug("Command: {}", action);
if (action.equals(ENABLE_TRACK) && !mTrackingEnabled) {
mErrorMsg = "";
mErrorTime = 0;
mTrackingEnabled = true;
mContinuous = false;
mDistanceNotified = 0f;
openDatabase();
mTrackingStarted = SystemClock.uptimeMillis();
mTrackStarted = System.currentTimeMillis();
mForegroundTracking = true;
updateDistanceTracked();
// https://developer.android.com/training/monitoring-device-state/doze-standby#support_for_other_use_cases
if (!mForegroundLocations)
if (Build.VERSION.SDK_INT < 34)
startForeground(NOTIFICATION_ID, getNotification());
else
startForeground(NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
logger.error("Command: {}", action);
if (action.equals(ENABLE_TRACK)) {
if (!mTrackingEnabled) { // Command can be sent on activity restart, while service already is running
mErrorMsg = "";
mErrorTime = 0;
mTrackingEnabled = true;
mContinuous = false;
mDistanceNotified = 0f;
openDatabase();
mTrackingStarted = SystemClock.uptimeMillis();
mTrackStarted = System.currentTimeMillis();
mForegroundTracking = true;
updateDistanceTracked();
// https://developer.android.com/training/monitoring-device-state/doze-standby#support_for_other_use_cases
if (!mForegroundLocations)
if (Build.VERSION.SDK_INT < 34)
startForeground(NOTIFICATION_ID, getNotification());
else
startForeground(NOTIFICATION_ID, getNotification(), ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
}
sendBroadcast(new Intent(BROADCAST_TRACK_STATE)
.putExtra("state", TRACKING_STATE.TRACKING.ordinal())
.setPackage(getPackageName()));
Configuration.setTrackingState(TRACKING_STATE.TRACKING.ordinal());
}
if (action.equals(DISABLE_TRACK) || action.equals(PAUSE_TRACK) && mTrackingEnabled) {
Expand All @@ -218,10 +223,16 @@ public int onStartCommand(Intent intent, int flags, int startId) {
long trackedTime = (SystemClock.uptimeMillis() - mTrackingStarted) / 60000;
Configuration.updateTrackingTime(trackedTime);
if (action.equals(DISABLE_TRACK)) {
Configuration.setTrackingState(TRACKING_STATE.DISABLED.ordinal());
sendBroadcast(new Intent(BROADCAST_TRACK_STATE)
.putExtra("state", TRACKING_STATE.DISABLED.ordinal())
.setPackage(getPackageName()));
Configuration.setTrackingState(TRACKING_STATE.PAUSED.ordinal());
tryToSaveTrack();
}
if (action.equals(PAUSE_TRACK)) {
sendBroadcast(new Intent(BROADCAST_TRACK_STATE)
.putExtra("state", TRACKING_STATE.PAUSED.ordinal())
.setPackage(getPackageName()));
Configuration.setTrackingState(TRACKING_STATE.PAUSED.ordinal());
}
if (!mForegroundLocations) {
Expand Down Expand Up @@ -399,7 +410,7 @@ private Notification getNotification() {
iLaunch.addCategory(Intent.CATEGORY_LAUNCHER);
iLaunch.setComponent(new ComponentName(getApplicationContext(), MainActivity.class));
iLaunch.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
PendingIntent piResult = PendingIntent.getActivity(this, 0, iLaunch, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
PendingIntent piResult = PendingIntent.getActivity(this, 0, iLaunch, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

builder.setWhen(mErrorTime);
builder.setSmallIcon(ntfId);
Expand Down
72 changes: 72 additions & 0 deletions app/src/main/java/mobi/maptrek/util/SingleLiveEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package mobi.maptrek.util;

import android.util.Log;

import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;

import java.util.concurrent.atomic.AtomicBoolean;

/**
* A lifecycle-aware observable that sends only new updates after subscription, used for events like
* navigation and Snackbar messages.
* <p>
* This avoids a common problem with events: on configuration change (like rotation) an update
* can be emitted if the observer is active. This LiveData only calls the observable if there's an
* explicit call to setValue() or call().
* <p>
* Note that only one observer is going to be notified of changes.
*/
public class SingleLiveEvent<T> extends MutableLiveData<T> {

private static final String TAG = "SingleLiveEvent";

private final AtomicBoolean mPending = new AtomicBoolean(false);

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
if (hasActiveObservers()) {
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
}
// Observe the internal MutableLiveData
super.observe(owner, t -> {
if (mPending.compareAndSet(true, false)) {
observer.onChanged(t);
}
});
}

@MainThread
public void setValue(@Nullable T value) {
mPending.set(true);
super.setValue(value);
}

/**
* Used for cases where T is Void, to make calls cleaner.
*/
@MainThread
public void call() {
setValue(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@

import mobi.maptrek.data.Track;
import mobi.maptrek.location.BaseLocationService.TRACKING_STATE;
import mobi.maptrek.util.SingleLiveEvent;

public class TrackViewModel extends ViewModel {
public final MutableLiveData<Track> selectedTrack = new MutableLiveData<>();
public final MutableLiveData<Track> currentTrack = new MutableLiveData<>();
public final MutableLiveData<TRACKING_STATE> trackingState = new MutableLiveData<>(TRACKING_STATE.DISABLED);
public final MutableLiveData<TRACKING_STATE> trackingCommand = new MutableLiveData<>(TRACKING_STATE.DISABLED);
public final SingleLiveEvent<TRACKING_STATE> trackingCommand = new SingleLiveEvent<>();
}

0 comments on commit dc023c7

Please sign in to comment.