forked from upstream-mirrors/OpenTracks
@@ -18,6 +18,9 @@ package de.dennisguse.opentracks;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment;
|
||||
import de.dennisguse.opentracks.fragments.ConfirmDeleteDialogFragment.ConfirmDeleteCaller;
|
||||
@@ -76,7 +79,7 @@ public abstract class AbstractListActivity extends AbstractActivity implements C
|
||||
getTrackRecordingServiceConnection().stopRecording(this, false);
|
||||
}
|
||||
Intent intent = IntentUtils.newIntent(this, TrackDeleteActivity.class);
|
||||
intent.putExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, trackIds);
|
||||
intent.putParcelableArrayListExtra(TrackDeleteActivity.EXTRA_TRACK_IDS, new ArrayList<>(Arrays.asList(trackIds)));
|
||||
startActivityForResult(intent, DELETE_REQUEST_CODE);
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
}
|
||||
|
||||
@@ -124,13 +124,13 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
contentProviderUtils = new ContentProviderUtils(this);
|
||||
sharedPreferences = PreferencesUtils.getSharedPreferences(this);
|
||||
|
||||
track = !trackId.isValid() ? contentProviderUtils.getTrack(trackId) : null;
|
||||
track = trackId != null ? contentProviderUtils.getTrack(trackId) : null;
|
||||
|
||||
listView = findViewById(R.id.marker_list);
|
||||
listView.setEmptyView(findViewById(R.id.marker_list_empty));
|
||||
listView.setOnItemClickListener((parent, view, position, id) -> {
|
||||
Intent intent = IntentUtils.newIntent(MarkerListActivity.this, MarkerDetailActivity.class)
|
||||
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, id);
|
||||
.putExtra(MarkerDetailActivity.EXTRA_MARKER_ID, new Waypoint.Id(id));
|
||||
startActivity(intent);
|
||||
});
|
||||
resourceCursorAdapter = new ResourceCursorAdapter(this, R.layout.list_item, null, 0) {
|
||||
@@ -167,7 +167,7 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
if (track != null) {
|
||||
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
||||
WaypointsColumns.TRACKID + "=?",
|
||||
new String[]{String.valueOf(track.getId())}, null);
|
||||
new String[]{String.valueOf(track.getId().getId())}, null);
|
||||
} else {
|
||||
return new CursorLoader(MarkerListActivity.this, WaypointsColumns.CONTENT_URI, PROJECTION,
|
||||
null, null, null);
|
||||
@@ -242,8 +242,8 @@ public class MarkerListActivity extends AbstractActivity implements DeleteMarker
|
||||
/**
|
||||
* Handles a context item selection.
|
||||
*
|
||||
* @param itemId the menu item id
|
||||
* @param waypointIds the marker ids
|
||||
* @param itemId the menu item id
|
||||
* @param longWaypointIds the marker ids
|
||||
* @return true if handled.
|
||||
*/
|
||||
private boolean handleContextItem(int itemId, long... longWaypointIds) {
|
||||
|
||||
@@ -21,6 +21,8 @@ import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.view.View;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.dennisguse.opentracks.content.data.Track;
|
||||
import de.dennisguse.opentracks.content.provider.ContentProviderUtils;
|
||||
import de.dennisguse.opentracks.util.SystemUtils;
|
||||
@@ -34,7 +36,7 @@ public class TrackDeleteActivity extends AbstractActivity {
|
||||
|
||||
public static final String EXTRA_TRACK_IDS = "track_ids";
|
||||
|
||||
private Track.Id[] trackIds;
|
||||
private List<Track.Id> trackIds;
|
||||
|
||||
private Thread deleteThread;
|
||||
|
||||
@@ -45,7 +47,7 @@ public class TrackDeleteActivity extends AbstractActivity {
|
||||
setResult(RESULT_CANCELED);
|
||||
|
||||
Intent intent = getIntent();
|
||||
trackIds = (Track.Id[]) intent.getParcelableArrayExtra(EXTRA_TRACK_IDS);
|
||||
trackIds = intent.getParcelableArrayListExtra(EXTRA_TRACK_IDS);
|
||||
deleteThread = new Thread(() -> {
|
||||
ContentProviderUtils contentProviderUtils = new ContentProviderUtils(TrackDeleteActivity.this);
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
||||
|
||||
// Preferences
|
||||
private boolean metricUnits = true;
|
||||
private Track.Id recordingTrackId = new Track.Id(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
private Track.Id recordingTrackId = null;
|
||||
|
||||
// Callback when an item is selected in the contextual action mode
|
||||
private final ContextualActionModeCallback contextualActionModeCallback = new ContextualActionModeCallback() {
|
||||
@@ -249,11 +249,11 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
||||
if (trackId == recordingTrackId.getId()) {
|
||||
// Is recording -> open record activity.
|
||||
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordingActivity.class)
|
||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
|
||||
} else {
|
||||
// Not recording -> open detail activity.
|
||||
newIntent = IntentUtils.newIntent(TrackListActivity.this, TrackRecordedActivity.class)
|
||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, trackId);
|
||||
.putExtra(TrackRecordedActivity.EXTRA_TRACK_ID, new Track.Id(trackId));
|
||||
}
|
||||
startActivity(newIntent);
|
||||
});
|
||||
@@ -479,8 +479,8 @@ public class TrackListActivity extends AbstractListActivity implements ConfirmDe
|
||||
/**
|
||||
* Handles a context item selection.
|
||||
*
|
||||
* @param itemId the menu item id
|
||||
* @param trackIds the track ids
|
||||
* @param itemId the menu item id
|
||||
* @param longTrackIds the track ids
|
||||
* @return true if handled.
|
||||
*/
|
||||
private boolean handleContextItem(int itemId, long... longTrackIds) {
|
||||
|
||||
@@ -133,7 +133,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
|
||||
// Update UI
|
||||
this.invalidateOptionsMenu();
|
||||
|
||||
if (!trackId.isValid()) {
|
||||
if (trackId.isValid()) {
|
||||
trackDataHub.loadTrack(trackId);
|
||||
}
|
||||
}
|
||||
@@ -251,7 +251,7 @@ public class TrackRecordedActivity extends AbstractListActivity implements Choos
|
||||
trackId = waypoint.getTrackId();
|
||||
}
|
||||
|
||||
if (!trackId.isValid()) {
|
||||
if (trackId == null) {
|
||||
Log.e(TAG, "TrackDetailActivity needs EXTRA_TRACK_ID.");
|
||||
finish();
|
||||
}
|
||||
|
||||
@@ -167,7 +167,8 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
trackId = savedInstanceState.getParcelable(EXTRA_TRACK_ID);
|
||||
} else {
|
||||
trackId = getIntent().getParcelableExtra(EXTRA_TRACK_ID);
|
||||
if (contentProviderUtils.getTrack(trackId) == null) {
|
||||
if (trackId != null && contentProviderUtils.getTrack(trackId) == null) {
|
||||
Log.w(TAG, "TrackId does not exists; cannot continue the recording.");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -247,7 +248,7 @@ public class TrackRecordingActivity extends AbstractActivity implements ChooseAc
|
||||
// Update UI
|
||||
this.invalidateOptionsMenu();
|
||||
|
||||
if (!trackId.isValid()) {
|
||||
if (trackId != null) {
|
||||
trackDataHub.loadTrack(trackId);
|
||||
trackController.onResume(true, recordingTrackPaused);
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ public class SearchEngine {
|
||||
score *= getTimeBoost(query, location.getTime());
|
||||
|
||||
// Score waypoints in the currently-selected track higher (searching inside the current track).
|
||||
if (query.currentTrackId.isValid() && waypoint.getTrackId().equals(query.currentTrackId)) {
|
||||
if (waypoint.getTrackId() != null && waypoint.getTrackId().equals(query.currentTrackId)) {
|
||||
score *= CURRENT_TRACK_WAYPOINT_PROMOTION;
|
||||
}
|
||||
|
||||
@@ -417,8 +417,8 @@ public class SearchEngine {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ScoredResult ["
|
||||
+ (track != null ? ("trackId=" + track.getId() + ", ") : "")
|
||||
+ (waypoint != null ? ("wptId=" + waypoint.getId() + ", ") : "")
|
||||
+ (track != null ? ("trackId=" + track.getId().getId() + ", ") : "")
|
||||
+ (waypoint != null ? ("wptId=" + waypoint.getId().getId() + ", ") : "")
|
||||
+ "score=" + score + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -174,10 +175,10 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
handler = null;
|
||||
}
|
||||
|
||||
public void loadTrack(final Track.Id trackId) {
|
||||
public void loadTrack(final @NonNull Track.Id trackId) {
|
||||
runInHandlerThread(() -> {
|
||||
if (trackId == selectedTrackId) {
|
||||
Log.i(TAG, "Not reloading track " + trackId);
|
||||
if (trackId.equals(selectedTrackId)) {
|
||||
Log.i(TAG, "Not reloading track " + trackId.getId());
|
||||
return;
|
||||
}
|
||||
selectedTrackId = trackId;
|
||||
@@ -212,14 +213,14 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
* Returns true if the selected track is recording.
|
||||
*/
|
||||
public boolean isSelectedTrackRecording() {
|
||||
return selectedTrackId == recordingTrackId && PreferencesUtils.isRecording(recordingTrackId);
|
||||
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && PreferencesUtils.isRecording(recordingTrackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the selected track is paused.
|
||||
*/
|
||||
public boolean isSelectedTrackPaused() {
|
||||
return selectedTrackId == recordingTrackId && recordingTrackPaused;
|
||||
return selectedTrackId != null && selectedTrackId.equals(recordingTrackId) && recordingTrackPaused;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -356,6 +357,11 @@ public class TrackDataHub implements SharedPreferences.OnSharedPreferenceChangeL
|
||||
long localLastSeenTrackPointIdId = updateSamplingState ? lastSeenTrackPointId : -1L;
|
||||
long maxPointId = updateSamplingState ? -1L : lastSeenTrackPointId;
|
||||
|
||||
if (selectedTrackId == null) {
|
||||
Log.w(TAG, "This should not happen, but it does"); //TODO
|
||||
return;
|
||||
}
|
||||
|
||||
long lastTrackPointId = contentProviderUtils.getLastTrackPointId(selectedTrackId);
|
||||
int samplingFrequency = -1;
|
||||
boolean includeNextPoint = false;
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
@@ -49,7 +50,11 @@ public class Track {
|
||||
public Track() {
|
||||
}
|
||||
|
||||
public Id getId() {
|
||||
/**
|
||||
* May be null if the track was not loaded from the database.
|
||||
*/
|
||||
public @Nullable
|
||||
Id getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -122,6 +127,7 @@ public class Track {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Deprecated //TODO Use a Track.Id of null instead
|
||||
public boolean isValid() {
|
||||
return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
}
|
||||
@@ -144,6 +150,12 @@ public class Track {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
throw new RuntimeException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel parcel, int i) {
|
||||
parcel.writeLong(id);
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -69,7 +70,11 @@ public final class Waypoint {
|
||||
this.photoUrl = photoUrl;
|
||||
}
|
||||
|
||||
public Id getId() {
|
||||
/**
|
||||
* May be null if the waypoint was not loaded from the database.
|
||||
*/
|
||||
public @Nullable
|
||||
Id getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -171,6 +176,7 @@ public final class Waypoint {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Deprecated //TODO Use a Waypoint.Id of null instead
|
||||
public boolean isValid() {
|
||||
return id != PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
}
|
||||
@@ -188,6 +194,12 @@ public final class Waypoint {
|
||||
return Objects.hash(id);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
throw new RuntimeException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.database.Cursor;
|
||||
import android.location.Location;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -186,6 +187,9 @@ public class ContentProviderUtils {
|
||||
* @param trackId the track id
|
||||
*/
|
||||
public void deleteTrack(Context context, Track.Id trackId) {
|
||||
if (trackId == null) {
|
||||
return;
|
||||
}
|
||||
deleteTrackPointsAndWaypoints(trackId);
|
||||
|
||||
// Delete track folder resources.
|
||||
@@ -200,7 +204,7 @@ public class ContentProviderUtils {
|
||||
*
|
||||
* @param trackId the track id
|
||||
*/
|
||||
private void deleteTrackPointsAndWaypoints(Track.Id trackId) {
|
||||
private void deleteTrackPointsAndWaypoints(@NonNull Track.Id trackId) {
|
||||
String[] selectionArgs = new String[]{Long.toString(trackId.getId())};
|
||||
|
||||
contentResolver.delete(TrackPointsColumns.CONTENT_URI_BY_ID, TrackPointsColumns.TRACKID + "=?", selectionArgs);
|
||||
@@ -297,7 +301,7 @@ public class ContentProviderUtils {
|
||||
TrackStatistics trackStatistics = track.getTrackStatistics();
|
||||
|
||||
// Value < 0 indicates no id is available
|
||||
if (track.getId().isValid()) {
|
||||
if (track.getId() != null && track.getId().isValid()) {
|
||||
values.put(TracksColumns._ID, track.getId().getId());
|
||||
}
|
||||
values.put(TracksColumns.UUID, UUIDUtils.toBytes(track.getUuid()));
|
||||
@@ -513,7 +517,7 @@ public class ContentProviderUtils {
|
||||
* @param waypoint the waypoint
|
||||
* @return the content provider URI of the inserted waypoint.
|
||||
*/
|
||||
public Uri insertWaypoint(Waypoint waypoint) {
|
||||
public Uri insertWaypoint(@NonNull Waypoint waypoint) {
|
||||
waypoint.setId(null);
|
||||
return contentResolver.insert(WaypointsColumns.CONTENT_URI, createContentValues(waypoint));
|
||||
}
|
||||
@@ -556,14 +560,16 @@ public class ContentProviderUtils {
|
||||
ContentValues createContentValues(@NonNull Waypoint waypoint) {
|
||||
ContentValues values = new ContentValues();
|
||||
|
||||
if (waypoint.getId().isValid()) {
|
||||
if (waypoint.getId() != null && waypoint.getId().isValid()) {
|
||||
values.put(WaypointsColumns._ID, waypoint.getId().getId());
|
||||
}
|
||||
values.put(WaypointsColumns.NAME, waypoint.getName());
|
||||
values.put(WaypointsColumns.DESCRIPTION, waypoint.getDescription());
|
||||
values.put(WaypointsColumns.CATEGORY, waypoint.getCategory());
|
||||
values.put(WaypointsColumns.ICON, waypoint.getIcon());
|
||||
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId().getId());
|
||||
if (waypoint.getTrackId() != null) {
|
||||
values.put(WaypointsColumns.TRACKID, waypoint.getTrackId().getId());
|
||||
}
|
||||
values.put(WaypointsColumns.LENGTH, waypoint.getLength());
|
||||
values.put(WaypointsColumns.DURATION, waypoint.getDuration());
|
||||
|
||||
@@ -693,7 +699,8 @@ public class ContentProviderUtils {
|
||||
*/
|
||||
@Deprecated
|
||||
public long getLastTrackPointId(Track.Id trackId) {
|
||||
if (!trackId.isValid()) {
|
||||
if (trackId == null || !trackId.isValid()) {
|
||||
Log.w(TAG, "Fix callers who do this.");
|
||||
return -1L;
|
||||
}
|
||||
String selection = TrackPointsColumns._ID + "=(SELECT MAX(" + TrackPointsColumns._ID + ") from " + TrackPointsColumns.TABLE_NAME + " WHERE " + TrackPointsColumns.TRACKID + "=?)";
|
||||
|
||||
@@ -86,7 +86,7 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
|
||||
|
||||
StringBuilder trackIdBuilder = new StringBuilder();
|
||||
for (Track.Id trackId : trackIds) {
|
||||
trackIdBuilder.append(trackId).append(TRACKID_DELIMITER);
|
||||
trackIdBuilder.append(trackId.getId()).append(TRACKID_DELIMITER);
|
||||
}
|
||||
trackIdBuilder.deleteCharAt(trackIdBuilder.lastIndexOf(TRACKID_DELIMITER));
|
||||
|
||||
@@ -221,7 +221,7 @@ public class ShareContentProvider extends CustomContentProvider implements ICont
|
||||
|
||||
PipeDataWriter<String> pipeDataWriter = (output, uri1, mimeType, opts, args) -> {
|
||||
try (FileOutputStream fileOutputStream = new FileOutputStream(output.getFileDescriptor())) {
|
||||
trackExporter.writeTrack(tracks, fileOutputStream);
|
||||
trackExporter.writeTrack(tracks.toArray(new Track[0]), fileOutputStream);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "there occurred an error while sharing a file: " + e);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
notificationManager = new TrackRecordingServiceNotificationManager(this);
|
||||
|
||||
// onSharedPreferenceChanged might not set recordingTrackId.
|
||||
recordingTrackId = new Track.Id(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT);
|
||||
recordingTrackId = null;
|
||||
|
||||
PreferencesUtils.register(this, sharedPreferenceChangeListener);
|
||||
sharedPreferenceChangeListener.onSharedPreferenceChanged(null, null);
|
||||
@@ -333,7 +333,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
if (track == null) {
|
||||
if (isRecording()) {
|
||||
Log.w(TAG, "track is null, but recordingTrackId not -1L. " + recordingTrackId);
|
||||
updateRecordingState(new Track.Id(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT), true);
|
||||
updateRecordingState(null, true);
|
||||
}
|
||||
showNotification(false);
|
||||
return;
|
||||
@@ -406,7 +406,7 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
Track.Id trackId = recordingTrackId;
|
||||
boolean wasPaused = recordingTrackPaused;
|
||||
|
||||
updateRecordingState(new Track.Id(PreferencesUtils.RECORDING_TRACK_ID_DEFAULT), true);
|
||||
updateRecordingState(null, true);
|
||||
|
||||
// Update database
|
||||
Track track = contentProviderUtils.getTrack(trackId);
|
||||
@@ -509,7 +509,8 @@ public class TrackRecordingService extends Service implements HandlerServer.Hand
|
||||
*/
|
||||
private void updateRecordingState(Track.Id trackId, boolean paused) {
|
||||
recordingTrackId = trackId;
|
||||
PreferencesUtils.setLong(this, R.string.recording_track_id_key, trackId.getId());
|
||||
long currentTrackId = trackId != null ? trackId.getId() : PreferencesUtils.RECORDING_TRACK_ID_DEFAULT;
|
||||
PreferencesUtils.setLong(this, R.string.recording_track_id_key, currentTrackId);
|
||||
recordingTrackPaused = paused;
|
||||
PreferencesUtils.setBoolean(this, R.string.recording_track_paused_key, recordingTrackPaused);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public interface TrackRecordingServiceInterface {
|
||||
boolean isPaused();
|
||||
|
||||
/**
|
||||
* Gets the current recording track ID. Returns -1 if not recording.
|
||||
* Gets the current recording track ID. Returns null if not recording.
|
||||
*/
|
||||
Track.Id getRecordingTrackId();
|
||||
|
||||
|
||||
@@ -354,7 +354,10 @@ public class PreferencesUtils {
|
||||
@Deprecated
|
||||
//TODO Method is very misleading: it only checks if the provided trackId not the default value (i.e., not recording).
|
||||
public static boolean isRecording(Track.Id recordingTrackId) {
|
||||
return recordingTrackId == null || recordingTrackId.getId() != RECORDING_TRACK_ID_DEFAULT;
|
||||
if (recordingTrackId == null) {
|
||||
return false;
|
||||
}
|
||||
return recordingTrackId.getId() != RECORDING_TRACK_ID_DEFAULT;
|
||||
}
|
||||
|
||||
public static void resetPreferences(Context context, boolean readAgain) {
|
||||
|
||||
Reference in New Issue
Block a user