some left out files and changes

Signed-off-by: kavikhalique <kavikhalique3@gmail.com>
This commit is contained in:
kavi khalique 2024-05-15 17:05:29 +05:30 committed by Roman Tsisyk
parent f1510351ef
commit 67a8e3f7a2
7 changed files with 376 additions and 19 deletions

View file

@ -26,6 +26,7 @@ import app.organicmaps.display.DisplayManager;
import app.organicmaps.downloader.CountryItem;
import app.organicmaps.downloader.MapManager;
import app.organicmaps.location.LocationHelper;
import app.organicmaps.location.LocationListener;
import app.organicmaps.location.LocationState;
import app.organicmaps.location.SensorHelper;
import app.organicmaps.location.TrackRecorder;
@ -38,8 +39,10 @@ import app.organicmaps.routing.RoutingController;
import app.organicmaps.search.SearchEngine;
import app.organicmaps.settings.StoragePathManager;
import app.organicmaps.sound.TtsPlayer;
import app.organicmaps.util.AppStateListener;
import app.organicmaps.util.Config;
import app.organicmaps.util.ConnectionState;
import app.organicmaps.util.Listeners;
import app.organicmaps.util.SharedPropertiesUtils;
import app.organicmaps.util.StorageUtils;
import app.organicmaps.util.ThemeSwitcher;
@ -50,6 +53,8 @@ import app.organicmaps.util.log.LogsManager;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MwmApplication extends Application implements Application.ActivityLifecycleCallbacks
@ -79,6 +84,7 @@ public class MwmApplication extends Application implements Application.ActivityL
private volatile boolean mFrameworkInitialized;
private volatile boolean mPlatformInitialized;
private final Listeners<AppStateListener> mAppStateListeners = new Listeners<>();
private Handler mMainLoopHandler;
private final Object mMainQueueToken = new Object();
@ -351,11 +357,25 @@ public class MwmApplication extends Application implements Application.ActivityL
nativeOnTransit(true);
mLocationHelper.resumeLocationInForeground();
Iterator<AppStateListener> listenerIterator = mAppStateListeners.iterator();
while(listenerIterator.hasNext())
{
AppStateListener listener = listenerIterator.next();
listener.onAppForeground();
}
mAppStateListeners.finishIterate();
}
private void onBackground()
{
Iterator<AppStateListener> listenerIterator = mAppStateListeners.iterator();
while(listenerIterator.hasNext())
{
AppStateListener listener = listenerIterator.next();
listener.onAppBackround();
}
mAppStateListeners.finishIterate();
Logger.d(TAG);
nativeOnTransit(false);
@ -398,4 +418,14 @@ public class MwmApplication extends Application implements Application.ActivityL
@Override
public void onProgress(String countryId, long localSize, long remoteSize) {}
}
public void addListener(AppStateListener listener)
{
mAppStateListeners.register(listener);
}
public void removeListener(AppStateListener listener)
{
mAppStateListeners.unregister(listener);
}
}

View file

@ -3,9 +3,11 @@ package app.organicmaps.location;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
@ -13,6 +15,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresPermission;
import androidx.annotation.UiThread;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.location.GnssStatusCompat;
import androidx.core.location.LocationManagerCompat;
@ -24,6 +27,7 @@ import app.organicmaps.bookmarks.data.FeatureId;
import app.organicmaps.bookmarks.data.MapObject;
import app.organicmaps.routing.JunctionInfo;
import app.organicmaps.routing.RoutingController;
import app.organicmaps.util.AppStateListener;
import app.organicmaps.util.Config;
import app.organicmaps.util.LocationUtils;
import app.organicmaps.util.NetworkPolicy;
@ -32,11 +36,14 @@ import app.organicmaps.util.log.Logger;
import java.util.LinkedHashSet;
import java.util.Set;
public class LocationHelper implements BaseLocationProvider.Listener
public class LocationHelper
implements BaseLocationProvider.Listener,
AppStateListener
{
private static final long INTERVAL_FOLLOW_MS = 0;
private static final long INTERVAL_NOT_FOLLOW_MS = 3000;
private static final long INTERVAL_NAVIGATION_MS = 0;
private static final long INTERVAL_TRACK_RECORDING_BACKGROUND = 10000;
private static final long AGPS_EXPIRATION_TIME_MS = 16 * 60 * 60 * 1000; // 16 hours
@ -79,6 +86,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
@Override
public void onSatelliteStatusChanged(@NonNull GnssStatusCompat status)
{
Logger.d(TAG,"value of interval "+ mInterval);
int used = 0;
boolean fixed = false;
for (int i = 0; i < status.getSatelliteCount(); i++)
@ -103,6 +111,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
{
mContext = context;
mLocationProvider = LocationProviderFactory.getProvider(mContext, this);
MwmApplication.from(mContext).addListener(this);
}
/**
@ -122,7 +131,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
if (mMyPosition == null)
mMyPosition = MapObject.createMapObject(FeatureId.EMPTY, MapObject.MY_POSITION, "", "",
mSavedLocation.getLatitude(), mSavedLocation.getLongitude());
mSavedLocation.getLatitude(), mSavedLocation.getLongitude());
return mMyPosition;
}
@ -160,18 +169,19 @@ public class LocationHelper implements BaseLocationProvider.Listener
}
LocationState.nativeLocationUpdated(mSavedLocation.getTime(),
mSavedLocation.getLatitude(),
mSavedLocation.getLongitude(),
mSavedLocation.getAccuracy(),
mSavedLocation.getAltitude(),
mSavedLocation.getSpeed(),
mSavedLocation.getBearing());
mSavedLocation.getLatitude(),
mSavedLocation.getLongitude(),
mSavedLocation.getAccuracy(),
mSavedLocation.getAltitude(),
mSavedLocation.getSpeed(),
mSavedLocation.getBearing());
}
@Override
public void onLocationChanged(@NonNull Location location)
{
Logger.d(TAG, "provider = " + mLocationProvider.getClass().getSimpleName() + " location = " + location);
Logger.d(TAG,"value of interval "+ mInterval);
if (!isActive())
{
@ -223,14 +233,15 @@ public class LocationHelper implements BaseLocationProvider.Listener
// Used by GoogleFusedLocationProvider.
@SuppressWarnings("unused")
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
@RequiresPermission(anyOf = { ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION })
@Override
@UiThread
public void onFusedLocationUnsupported()
{
// Try to downgrade to the native provider first and restart the service before notifying the user.
Logger.d(TAG, "provider = " + mLocationProvider.getClass().getSimpleName() + " is not supported," +
" downgrading to use native provider");
Logger.d(TAG, "provider = " + mLocationProvider.getClass()
.getSimpleName() + " is not supported," +
" downgrading to use native provider");
mLocationProvider.stop();
mLocationProvider = new AndroidNativeProvider(mContext, this);
mActive = true;
@ -253,7 +264,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
public void onLocationDisabled()
{
Logger.d(TAG, "provider = " + mLocationProvider.getClass().getSimpleName() +
" settings = " + LocationUtils.areLocationServicesTurnedOn(mContext));
" settings = " + LocationUtils.areLocationServicesTurnedOn(mContext));
stop();
LocationState.nativeOnLocationError(LocationState.ERROR_GPS_OFF);
@ -306,7 +317,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
/**
* Restart the location with a new refresh interval if changed.
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
@RequiresPermission(anyOf = { ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION })
public void restartWithNewMode()
{
if (!isActive())
@ -328,7 +339,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
/**
* Starts polling location updates.
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
@RequiresPermission(anyOf = { ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION })
public void start()
{
if (isActive())
@ -346,7 +357,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
final long oldInterval = mInterval;
mInterval = calcLocationUpdatesInterval();
Logger.i(TAG, "provider = " + mLocationProvider.getClass().getSimpleName() +
" mInFirstRun = " + mInFirstRun + " oldInterval = " + oldInterval + " interval = " + mInterval);
" mInFirstRun = " + mInFirstRun + " oldInterval = " + oldInterval + " interval = " + mInterval);
mActive = true;
mLocationProvider.start(mInterval);
subscribeToGnssStatusUpdates();
@ -394,8 +405,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
Logger.i(TAG, "Permissions ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION are not granted");
return;
}
start();
restartWithNewMode();
}
private void checkForAgpsUpdates()
@ -426,7 +436,7 @@ public class LocationHelper implements BaseLocationProvider.Listener
return;
final LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
LocationManagerCompat.registerGnssStatusCallback(locationManager, ContextCompat.getMainExecutor(mContext),
mGnssStatusCallback);
mGnssStatusCallback);
}
private void unsubscribeFromGnssStatusUpdates()
@ -466,4 +476,28 @@ public class LocationHelper implements BaseLocationProvider.Listener
Framework.nativeRunFirstLaunchAnimation();
}
}
@Override
public void onAppBackround()
{
Logger.i("kavi", "Location helper knows app went in background");
if (!RoutingController.get().isNavigating() && isActive() && TrackRecorder.nativeIsEnabled() && mInterval != INTERVAL_TRACK_RECORDING_BACKGROUND)
{
Logger.i(TAG, "update refresh interval: old = " + mInterval + " new = " + INTERVAL_TRACK_RECORDING_BACKGROUND);
if (LocationUtils.checkLocationPermission(mContext))
{
mLocationProvider.stop();
mInterval = INTERVAL_TRACK_RECORDING_BACKGROUND;
mLocationProvider.start(INTERVAL_TRACK_RECORDING_BACKGROUND);
}
}
Logger.i("kavi","interval is "+ mInterval);
}
@Override
public void onAppForeground()
{
Logger.i("kavi", "Location helper knows app came in foreground");
resumeLocationInForeground();
}
}

View file

@ -0,0 +1,9 @@
package app.organicmaps.location;
public abstract class TrackRecorder
{
public static native void nativeSetEnabled(boolean enable);
public static native boolean nativeIsEnabled();
public static native void nativeSetDuration(int hours);
public static native int nativeGetDuration();
}

View file

@ -0,0 +1,170 @@
package app.organicmaps.location;
import android.app.ForegroundServiceStartNotAllowedException;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Build;
import android.os.IBinder;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RequiresPermission;
import androidx.core.app.NotificationChannelCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import app.organicmaps.MwmActivity;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.settings.TrackRecordSettingsFragment;
import app.organicmaps.util.LocationUtils;
import app.organicmaps.util.log.Logger;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
public class TrackRecordingService extends Service implements LocationListener
{
public static final String TRACK_REC_CHANNEL_ID = "TRACK RECORDING";
public static final int TRACK_REC_NOTIFICATION_ID = 54321;
private static NotificationCompat.Builder mNotificationBuilder;
private static final String TAG = TrackRecordingService.class.getSimpleName();
@Nullable
@Override
public IBinder onBind(Intent intent)
{
return null;
}
@RequiresPermission(value = ACCESS_FINE_LOCATION)
public static void startForegroundService(@NonNull Context context)
{
ContextCompat.startForegroundService(context, new Intent(context, TrackRecordingService.class));
}
public static void createNotificationChannel(@NonNull Context context)
{
final NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
final NotificationChannelCompat channel = new NotificationChannelCompat.Builder(TRACK_REC_CHANNEL_ID,
NotificationManagerCompat.IMPORTANCE_LOW)
.setName("Track Recording")
.setLightsEnabled(false)
.setVibrationEnabled(false)
.build();
notificationManager.createNotificationChannel(channel);
}
@NonNull
public static NotificationCompat.Builder getNotificationBuilder(@NonNull Context context)
{
if (mNotificationBuilder != null)
return mNotificationBuilder;
final int FLAG_IMMUTABLE = Build.VERSION.SDK_INT < Build.VERSION_CODES.M ? 0 : PendingIntent.FLAG_IMMUTABLE;
final Intent contentIntent = new Intent(context, MwmActivity.class);
final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, contentIntent,
PendingIntent.FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
mNotificationBuilder = new NotificationCompat.Builder(context, TRACK_REC_CHANNEL_ID)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.setPriority(NotificationManager.IMPORTANCE_LOW)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setOngoing(true)
.setShowWhen(true)
.setOnlyAlertOnce(true)
.setSmallIcon(R.drawable.ic_splash)
.setContentTitle("Track Recording is Running")
.setContentText("Recording your traversed tracks in background")
.setContentIntent(pendingIntent)
.setColor(ContextCompat.getColor(context, R.color.notification));
return mNotificationBuilder;
}
@Override
public void onDestroy()
{
mNotificationBuilder = null;
// The notification is cancelled automatically by the system.
}
@Override
public int onStartCommand(@NonNull Intent intent, int flags, int startId)
{
if (!MwmApplication.from(this).arePlatformAndCoreInitialized())
{
Logger.w(TAG, "Application is not initialized");
stopSelf();
return START_NOT_STICKY; // The service will be stopped by stopSelf().
}
if (!LocationUtils.checkFineLocationPermission(this))
{
// In a hypothetical scenario, the user could revoke location permissions after the app's process crashed,
// but before the service with START_STICKY was restarted by the system.
Logger.w(TAG, "Permission ACCESS_FINE_LOCATION is not granted, skipping TrackRecordingService");
stopSelf();
return START_NOT_STICKY; // The service will be stopped by stopSelf().
}
if(!TrackRecorder.nativeIsEnabled())
{
Logger.i("kavi","Service can't be started because Track Recorder is turned off in settings");
stopSelf();
return START_NOT_STICKY;
}
Logger.i(TAG, "Starting foreground");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
{
try
{
startForeground(TrackRecordingService.TRACK_REC_NOTIFICATION_ID, getNotificationBuilder(this).build());
} catch (ForegroundServiceStartNotAllowedException e)
{
Logger.e(TAG, "Oops! ForegroundService is not allowed", e);
}
}
else
{
startForeground(TrackRecordingService.TRACK_REC_NOTIFICATION_ID, getNotificationBuilder(this).build());
}
final LocationHelper locationHelper = LocationHelper.from(this);
// Subscribe to location updates. This call is idempotent.
locationHelper.addListener(this);
// Restart the location with more frequent refresh interval for navigation.
locationHelper.restartWithNewMode();
return START_NOT_STICKY;
}
public static void stopService(@NonNull Context context)
{
Logger.i(TAG);
context.stopService(new Intent(context, TrackRecordingService.class));
}
@Override
public void onLocationUpdated(@NonNull Location location)
{
Logger.i(TAG,"Location is being updated in Track Recording service");
}
@Override
public void onLocationDisabled()
{
LocationListener.super.onLocationDisabled();
TrackRecorder.nativeSetEnabled(false);
stopSelf();
}
}

View file

@ -0,0 +1,89 @@
package app.organicmaps.settings;
import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
import androidx.preference.TwoStatePreference;
import app.organicmaps.MwmApplication;
import app.organicmaps.R;
import app.organicmaps.location.TrackRecorder;
import app.organicmaps.location.TrackRecordingService;
import app.organicmaps.util.LocationUtils;
import app.organicmaps.util.log.Logger;
import static app.organicmaps.location.TrackRecordingService.stopService;
public class TrackRecordSettingsFragment extends BaseXmlSettingsFragment
{
private ListPreference mRecordTime;
private TwoStatePreference mRecentTrack;
@Override
protected int getXmlResources()
{
return R.xml.pref_track_record;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
{
super.onViewCreated(view, savedInstanceState);
mRecentTrack = getPreference(getString(R.string.pref_recent_track));
mRecordTime = getPreference(getString(R.string.pref_track_record_time));
mRecordTime.setValue(Integer.toString(TrackRecorder.nativeGetDuration()));
mRecentTrack.setChecked(TrackRecorder.nativeIsEnabled());
mRecordTime.setEnabled(TrackRecorder.nativeIsEnabled());
mRecentTrack.setOnPreferenceChangeListener(((preference, newValue) -> {
if (newValue == null)
return false;
boolean newVal = (boolean) newValue;
if (newVal)
{
if (ActivityCompat.checkSelfPermission(getSettingsActivity(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED)
{
Toast.makeText(getActivity(),"Please give permission of precise location access",Toast.LENGTH_SHORT).show();
return false;
}
if(!LocationUtils.areLocationServicesTurnedOn(MwmApplication.from(requireContext()))){
Toast.makeText(getActivity(),"Please turn on location",Toast.LENGTH_SHORT).show();
return false;
}
TrackRecordingService.startForegroundService(MwmApplication.from(getSettingsActivity()));
}
else
{
stopService(MwmApplication.from(requireContext()));
}
mRecordTime.setEnabled(newVal);
TrackRecorder.nativeSetEnabled(newVal);
return true;
}));
mRecordTime.setOnPreferenceChangeListener((preference, newValue) -> {
if (newValue == null)
return false;
String newVal = (String) newValue;
if(newVal.equals(mRecordTime.getValue()))
return false;
int hours = Integer.parseInt(newVal);
TrackRecorder.nativeSetDuration(hours);
return true;
});
}
}

View file

@ -0,0 +1,7 @@
package app.organicmaps.util;
public interface AppStateListener
{
void onAppBackround();
void onAppForeground();
}

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<SwitchPreferenceCompat
android:key="@string/pref_recent_track"
android:title="Record Recent Track"
android:order="1"/>
<ListPreference
android:key="@string/pref_track_record_time"
android:title="Record time"
app:singleLineTitle="false"
android:summary="Select duration for which recorded point will be shown on map"
android:entries="@array/track_length"
android:entryValues="@array/track_length_values"
android:order="2"/>
</androidx.preference.PreferenceScreen>