[android][sdk] Refactor shared preferences

Signed-off-by: Andrei Shkrob <github@shkrob.dev>
This commit is contained in:
Andrei Shkrob
2025-06-29 22:07:12 +02:00
committed by Konstantin Pastbin
parent c600a4fd5d
commit 40adb0f860
18 changed files with 137 additions and 127 deletions

View File

@@ -347,10 +347,10 @@ public class MwmActivity extends BaseMwmFragmentActivity
private void migrateOAuthCredentials() private void migrateOAuthCredentials()
{ {
if (OsmOAuth.containsOAuth1Credentials(this)) if (OsmOAuth.containsOAuth1Credentials())
{ {
// Remove old OAuth v1 secrets // Remove old OAuth v1 secrets
OsmOAuth.clearOAuth1Credentials(this); OsmOAuth.clearOAuth1Credentials();
// Notify user to re-login // Notify user to re-login
dismissAlertDialog(); dismissAlertDialog();

View File

@@ -31,7 +31,6 @@ import app.organicmaps.sdk.maplayer.subway.SubwayManager;
import app.organicmaps.sdk.util.Config; import app.organicmaps.sdk.util.Config;
import app.organicmaps.sdk.util.ConnectionState; import app.organicmaps.sdk.util.ConnectionState;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.sdk.util.log.LogsManager;
import app.organicmaps.util.Utils; import app.organicmaps.util.Utils;
import java.io.IOException; import java.io.IOException;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
@@ -52,6 +51,10 @@ public class MwmApplication extends Application implements Application.ActivityL
@Nullable @Nullable
private WeakReference<Activity> mTopActivity; private WeakReference<Activity> mTopActivity;
@SuppressWarnings("NotNullFieldNotInitialized")
@NonNull
public static MwmApplication sInstance;
@UiThread @UiThread
@Nullable @Nullable
public Activity getTopActivity() public Activity getTopActivity()
@@ -101,13 +104,10 @@ public class MwmApplication extends Application implements Application.ActivityL
return (MwmApplication) context.getApplicationContext(); return (MwmApplication) context.getApplicationContext();
} }
@NonNull
public static MwmApplication sInstance;
@NonNull @NonNull
public static SharedPreferences prefs(@NonNull Context context) public static SharedPreferences prefs(@NonNull Context context)
{ {
return context.getSharedPreferences(context.getString(R.string.pref_file_name), MODE_PRIVATE); return from(context).getOrganicMaps().getPreferences();
} }
@Override @Override
@@ -120,8 +120,6 @@ public class MwmApplication extends Application implements Application.ActivityL
mOrganicMaps = new OrganicMaps(getApplicationContext()); mOrganicMaps = new OrganicMaps(getApplicationContext());
LogsManager.INSTANCE.initFileLogging(this);
ConnectionState.INSTANCE.initialize(this); ConnectionState.INSTANCE.initialize(this);
DownloaderNotifier.createNotificationChannel(this); DownloaderNotifier.createNotificationChannel(this);

View File

@@ -32,7 +32,7 @@ public class OsmUploadWork extends Worker
*/ */
public static void startActionUploadOsmChanges(@NonNull Context context) public static void startActionUploadOsmChanges(@NonNull Context context)
{ {
if (Editor.nativeHasSomethingToUpload() && OsmOAuth.isAuthorized(context)) if (Editor.nativeHasSomethingToUpload() && OsmOAuth.isAuthorized())
{ {
final Constraints c = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build(); final Constraints c = new Constraints.Builder().setRequiredNetworkType(NetworkType.CONNECTED).build();
final WorkRequest wr = new OneTimeWorkRequest.Builder(OsmUploadWork.class).setConstraints(c).build(); final WorkRequest wr = new OneTimeWorkRequest.Builder(OsmUploadWork.class).setConstraints(c).build();
@@ -44,13 +44,12 @@ public class OsmUploadWork extends Worker
@Override @Override
public Result doWork() public Result doWork()
{ {
final MwmApplication app = MwmApplication.from(mContext); if (!MwmApplication.from(mContext).getOrganicMaps().arePlatformAndCoreInitialized())
if (!app.getOrganicMaps().arePlatformAndCoreInitialized())
{ {
Logger.w(TAG, "Application is not initialized, ignoring " + mWorkerParameters); Logger.w(TAG, "Application is not initialized, ignoring " + mWorkerParameters);
return Result.failure(); return Result.failure();
} }
Editor.uploadChanges(mContext); Editor.uploadChanges();
return Result.success(); return Result.success();
} }
} }

View File

@@ -356,8 +356,7 @@ public class EditorHostFragment
private void processEditedFeatures() private void processEditedFeatures()
{ {
Context context = requireContext(); if (OsmOAuth.isAuthorized())
if (OsmOAuth.isAuthorized(context))
{ {
Utils.navigateToParent(requireActivity()); Utils.navigateToParent(requireActivity());
return; return;

View File

@@ -138,7 +138,7 @@ public class OsmLoginFragment extends BaseMwmToolbarFragment
private void onAuthSuccess(String oauthToken, String username) private void onAuthSuccess(String oauthToken, String username)
{ {
OsmOAuth.setAuthorization(requireContext(), oauthToken, username); OsmOAuth.setAuthorization(oauthToken, username);
final Bundle extras = requireActivity().getIntent().getExtras(); final Bundle extras = requireActivity().getIntent().getExtras();
if (extras != null && extras.getBoolean("redirectToProfile", false)) if (extras != null && extras.getBoolean("redirectToProfile", false))
startActivity(new Intent(requireContext(), ProfileActivity.class)); startActivity(new Intent(requireContext(), ProfileActivity.class));

View File

@@ -60,9 +60,9 @@ public class ProfileFragment extends BaseMwmToolbarFragment
view.findViewById(R.id.about_osm) view.findViewById(R.id.about_osm)
.setOnClickListener((v) -> Utils.openUrl(requireActivity(), getString(R.string.osm_wiki_about_url))); .setOnClickListener((v) -> Utils.openUrl(requireActivity(), getString(R.string.osm_wiki_about_url)));
view.findViewById(R.id.osm_history) view.findViewById(R.id.osm_history)
.setOnClickListener((v) -> Utils.openUrl(requireActivity(), OsmOAuth.getHistoryUrl(requireContext()))); .setOnClickListener((v) -> Utils.openUrl(requireActivity(), OsmOAuth.getHistoryUrl()));
view.findViewById(R.id.osm_notes) view.findViewById(R.id.osm_notes)
.setOnClickListener((v) -> Utils.openUrl(requireActivity(), OsmOAuth.getNotesUrl(requireContext()))); .setOnClickListener((v) -> Utils.openUrl(requireActivity(), OsmOAuth.getNotesUrl()));
View buttonsContainer = view.findViewById(R.id.buttons_container); View buttonsContainer = view.findViewById(R.id.buttons_container);
ViewCompat.setOnApplyWindowInsetsListener( ViewCompat.setOnApplyWindowInsetsListener(
@@ -75,7 +75,7 @@ public class ProfileFragment extends BaseMwmToolbarFragment
private void refreshViews() private void refreshViews()
{ {
if (OsmOAuth.isAuthorized(requireContext())) if (OsmOAuth.isAuthorized())
{ {
// Update the number of uploaded changesets from OSM. // Update the number of uploaded changesets from OSM.
ThreadPool.getWorker().execute(() -> { ThreadPool.getWorker().execute(() -> {
@@ -84,9 +84,9 @@ public class ProfileFragment extends BaseMwmToolbarFragment
UiUtils.show(mProfileInfoLoading); UiUtils.show(mProfileInfoLoading);
UiUtils.hide(mUserInfoBlock); UiUtils.hide(mUserInfoBlock);
} }
final int profileEditCount = OsmOAuth.getOsmChangesetsCount(requireContext(), getParentFragmentManager()); final int profileEditCount = OsmOAuth.getOsmChangesetsCount(getParentFragmentManager());
final String profileUsername = OsmOAuth.getUsername(requireContext()); final String profileUsername = OsmOAuth.getUsername();
final Bitmap profilePicture = OsmOAuth.getProfilePicture(requireContext()); final Bitmap profilePicture = OsmOAuth.getProfilePicture();
UiThread.run(() -> { UiThread.run(() -> {
mEditsSent.setText(NumberFormat.getInstance().format(profileEditCount)); mEditsSent.setText(NumberFormat.getInstance().format(profileEditCount));
@@ -118,7 +118,7 @@ public class ProfileFragment extends BaseMwmToolbarFragment
.setMessage(R.string.osm_log_out_confirmation) .setMessage(R.string.osm_log_out_confirmation)
.setPositiveButton(R.string.yes, .setPositiveButton(R.string.yes,
(dialog, which) -> { (dialog, which) -> {
OsmOAuth.clearAuthorization(requireContext()); OsmOAuth.clearAuthorization();
refreshViews(); refreshViews();
}) })
.setNegativeButton(R.string.no, null) .setNegativeButton(R.string.no, null)

View File

@@ -43,7 +43,7 @@ public class LayersAdapter extends RecyclerView.Adapter<LayerHolder>
holder.mButton.setContentDescription(context.getString(item.getTitle())); holder.mButton.setContentDescription(context.getString(item.getTitle()));
holder.mTitle.setSelected(isEnabled); holder.mTitle.setSelected(isEnabled);
holder.mTitle.setText(item.getTitle()); holder.mTitle.setText(item.getTitle());
boolean isNewLayer = SharedPropertiesUtils.shouldShowNewMarkerForLayerMode(context, item.getMode()); boolean isNewLayer = SharedPropertiesUtils.shouldShowNewMarkerForLayerMode(item.getMode());
UiUtils.showIf(isNewLayer, holder.mNewMarker); UiUtils.showIf(isNewLayer, holder.mNewMarker);
holder.mButton.setImageResource(isEnabled ? item.getEnabledStateDrawable() : item.getDisabledStateDrawable()); holder.mButton.setImageResource(isEnabled ? item.getEnabledStateDrawable() : item.getDisabledStateDrawable());
holder.mListener = item::onClick; holder.mListener = item::onClick;

View File

@@ -70,7 +70,7 @@ public class ToggleMapLayerFragment extends Fragment
{ {
Mode mode = item.getMode(); Mode mode = item.getMode();
Context context = v.getContext(); Context context = v.getContext();
SharedPropertiesUtils.setLayerMarkerShownForLayerMode(context, mode); SharedPropertiesUtils.setLayerMarkerShownForLayerMode(mode);
mode.setEnabled(context, !mode.isEnabled(context)); mode.setEnabled(context, !mode.isEnabled(context));
mAdapter.notifyDataSetChanged(); mAdapter.notifyDataSetChanged();
mMapButtonsController.updateLayerButton(); mMapButtonsController.updateLayerButton();

View File

@@ -1,6 +1,7 @@
package app.organicmaps.sdk; package app.organicmaps.sdk;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleOwner;
@@ -9,6 +10,7 @@ import app.organicmaps.R;
import app.organicmaps.routing.RoutingController; import app.organicmaps.routing.RoutingController;
import app.organicmaps.sdk.bookmarks.data.BookmarkManager; import app.organicmaps.sdk.bookmarks.data.BookmarkManager;
import app.organicmaps.sdk.downloader.Android7RootCertificateWorkaround; import app.organicmaps.sdk.downloader.Android7RootCertificateWorkaround;
import app.organicmaps.sdk.editor.OsmOAuth;
import app.organicmaps.sdk.location.LocationHelper; import app.organicmaps.sdk.location.LocationHelper;
import app.organicmaps.sdk.location.SensorHelper; import app.organicmaps.sdk.location.SensorHelper;
import app.organicmaps.sdk.maplayer.isolines.IsolinesManager; import app.organicmaps.sdk.maplayer.isolines.IsolinesManager;
@@ -22,6 +24,7 @@ import app.organicmaps.sdk.util.StorageUtils;
import app.organicmaps.sdk.util.ThemeSwitcher; import app.organicmaps.sdk.util.ThemeSwitcher;
import app.organicmaps.sdk.util.UiUtils; import app.organicmaps.sdk.util.UiUtils;
import app.organicmaps.sdk.util.log.Logger; import app.organicmaps.sdk.util.log.Logger;
import app.organicmaps.sdk.util.log.LogsManager;
import app.organicmaps.settings.StoragePathManager; import app.organicmaps.settings.StoragePathManager;
import java.io.IOException; import java.io.IOException;
@@ -32,6 +35,9 @@ public final class OrganicMaps implements DefaultLifecycleObserver
@NonNull @NonNull
private final Context mContext; private final Context mContext;
@NonNull
private final SharedPreferences mPreferences;
@NonNull @NonNull
private final IsolinesManager mIsolinesManager; private final IsolinesManager mIsolinesManager;
@NonNull @NonNull
@@ -72,6 +78,8 @@ public final class OrganicMaps implements DefaultLifecycleObserver
public OrganicMaps(@NonNull Context context) public OrganicMaps(@NonNull Context context)
{ {
mContext = context.getApplicationContext(); mContext = context.getApplicationContext();
mPreferences = mContext.getSharedPreferences(context.getString(app.organicmaps.sdk.R.string.pref_file_name),
Context.MODE_PRIVATE);
// Set configuration directory as early as possible. // Set configuration directory as early as possible.
// Other methods may explicitly use Config, which requires settingsDir to be set. // Other methods may explicitly use Config, which requires settingsDir to be set.
@@ -81,7 +89,10 @@ public final class OrganicMaps implements DefaultLifecycleObserver
Logger.d(TAG, "Settings path = " + settingsPath); Logger.d(TAG, "Settings path = " + settingsPath);
nativeSetSettingsDir(settingsPath); nativeSetSettingsDir(settingsPath);
Config.init(mContext); Config.init(mContext, mPreferences);
OsmOAuth.init(mPreferences);
SharedPropertiesUtils.init(mPreferences);
LogsManager.INSTANCE.initFileLogging(mContext, mPreferences);
Android7RootCertificateWorkaround.initializeIfNeeded(mContext); Android7RootCertificateWorkaround.initializeIfNeeded(mContext);
@@ -120,6 +131,12 @@ public final class OrganicMaps implements DefaultLifecycleObserver
nativeOnTransit(false); nativeOnTransit(false);
} }
@NonNull
public SharedPreferences getPreferences()
{
return mPreferences;
}
private void initNativePlatform() throws IOException private void initNativePlatform() throws IOException
{ {
if (mPlatformInitialized) if (mPlatformInitialized)
@@ -143,7 +160,7 @@ public final class OrganicMaps implements DefaultLifecycleObserver
nativeInitPlatform(mContext, apkPath, writablePath, privatePath, tempPath, app.organicmaps.BuildConfig.FLAVOR, nativeInitPlatform(mContext, apkPath, writablePath, privatePath, tempPath, app.organicmaps.BuildConfig.FLAVOR,
app.organicmaps.BuildConfig.BUILD_TYPE, UiUtils.isTablet(mContext)); app.organicmaps.BuildConfig.BUILD_TYPE, UiUtils.isTablet(mContext));
Config.setStoragePath(writablePath); Config.setStoragePath(writablePath);
Config.setStatisticsEnabled(SharedPropertiesUtils.isStatisticsEnabled(mContext)); Config.setStatisticsEnabled(SharedPropertiesUtils.isStatisticsEnabled());
mPlatformInitialized = true; mPlatformInitialized = true;
Logger.i(TAG, "Platform initialized"); Logger.i(TAG, "Platform initialized");

View File

@@ -1,6 +1,5 @@
package app.organicmaps.sdk.editor; package app.organicmaps.sdk.editor;
import android.content.Context;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Size; import androidx.annotation.Size;
@@ -43,10 +42,10 @@ public final class Editor
private static native void nativeInit(); private static native void nativeInit();
@WorkerThread @WorkerThread
public static void uploadChanges(@NonNull Context context) public static void uploadChanges()
{ {
if (nativeHasSomethingToUpload() && OsmOAuth.isAuthorized(context)) if (nativeHasSomethingToUpload() && OsmOAuth.isAuthorized())
nativeUploadChanges(OsmOAuth.getAuthToken(context), BuildConfig.VERSION_NAME, BuildConfig.APPLICATION_ID); nativeUploadChanges(OsmOAuth.getAuthToken(), BuildConfig.VERSION_NAME, BuildConfig.APPLICATION_ID);
} }
public static native boolean nativeShouldShowEditPlace(); public static native boolean nativeShouldShowEditPlace();

View File

@@ -1,6 +1,5 @@
package app.organicmaps.sdk.editor; package app.organicmaps.sdk.editor;
import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@@ -8,7 +7,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.Size; import androidx.annotation.Size;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import app.organicmaps.MwmApplication;
import app.organicmaps.sdk.util.NetworkPolicy; import app.organicmaps.sdk.util.NetworkPolicy;
public final class OsmOAuth public final class OsmOAuth
@@ -28,7 +26,9 @@ public final class OsmOAuth
} }
} }
public static final int OK = 0; @SuppressWarnings("NotNullFieldNotInitialized")
@NonNull
private static SharedPreferences mPrefs;
private static final String PREF_OSM_TOKEN = "OsmToken"; // Unused after migration from OAuth1 to OAuth2 private static final String PREF_OSM_TOKEN = "OsmToken"; // Unused after migration from OAuth1 to OAuth2
private static final String PREF_OSM_SECRET = "OsmSecret"; // Unused after migration from OAuth1 to OAuth2 private static final String PREF_OSM_SECRET = "OsmSecret"; // Unused after migration from OAuth1 to OAuth2
@@ -38,51 +38,50 @@ public final class OsmOAuth
public static final String URL_PARAM_VERIFIER = "oauth_verifier"; public static final String URL_PARAM_VERIFIER = "oauth_verifier";
public static boolean isAuthorized(@NonNull Context context) public static void init(@NonNull SharedPreferences prefs)
{ {
return MwmApplication.prefs(context).contains(PREF_OSM_OAUTH2_TOKEN); mPrefs = prefs;
} }
public static boolean containsOAuth1Credentials(@NonNull Context context) public static boolean isAuthorized()
{ {
SharedPreferences prefs = MwmApplication.prefs(context); return mPrefs.contains(PREF_OSM_OAUTH2_TOKEN);
return prefs.contains(PREF_OSM_TOKEN) && prefs.contains(PREF_OSM_SECRET);
} }
public static void clearOAuth1Credentials(@NonNull Context context) public static boolean containsOAuth1Credentials()
{ {
MwmApplication.prefs(context).edit().remove(PREF_OSM_TOKEN).remove(PREF_OSM_SECRET).apply(); return mPrefs.contains(PREF_OSM_TOKEN) && mPrefs.contains(PREF_OSM_SECRET);
} }
public static String getAuthToken(@NonNull Context context) public static void clearOAuth1Credentials()
{ {
return MwmApplication.prefs(context).getString(PREF_OSM_OAUTH2_TOKEN, ""); mPrefs.edit().remove(PREF_OSM_TOKEN).remove(PREF_OSM_SECRET).apply();
} }
public static String getUsername(@NonNull Context context) public static String getAuthToken()
{ {
return MwmApplication.prefs(context).getString(PREF_OSM_USERNAME, ""); return mPrefs.getString(PREF_OSM_OAUTH2_TOKEN, "");
} }
public static Bitmap getProfilePicture(@NonNull Context context) public static String getUsername()
{
return mPrefs.getString(PREF_OSM_USERNAME, "");
}
public static Bitmap getProfilePicture()
{ {
// TODO(HB): load and store image in cache here // TODO(HB): load and store image in cache here
return null; return null;
} }
public static void setAuthorization(@NonNull Context context, String oauthToken, String username) public static void setAuthorization(String oauthToken, String username)
{ {
MwmApplication.prefs(context) mPrefs.edit().putString(PREF_OSM_OAUTH2_TOKEN, oauthToken).putString(PREF_OSM_USERNAME, username).apply();
.edit()
.putString(PREF_OSM_OAUTH2_TOKEN, oauthToken)
.putString(PREF_OSM_USERNAME, username)
.apply();
} }
public static void clearAuthorization(@NonNull Context context) public static void clearAuthorization()
{ {
MwmApplication.prefs(context) mPrefs.edit()
.edit()
.remove(PREF_OSM_TOKEN) .remove(PREF_OSM_TOKEN)
.remove(PREF_OSM_SECRET) .remove(PREF_OSM_SECRET)
.remove(PREF_OSM_USERNAME) .remove(PREF_OSM_USERNAME)
@@ -90,14 +89,16 @@ public final class OsmOAuth
.apply(); .apply();
} }
public static String getHistoryUrl(@NonNull Context context) @NonNull
public static String getHistoryUrl()
{ {
return nativeGetHistoryUrl(getUsername(context)); return nativeGetHistoryUrl(getUsername());
} }
public static String getNotesUrl(@NonNull Context context) @NonNull
public static String getNotesUrl()
{ {
return nativeGetNotesUrl(getUsername(context)); return nativeGetNotesUrl(getUsername());
} }
/* /*
@@ -144,21 +145,20 @@ public final class OsmOAuth
private static native int nativeGetOsmChangesetsCount(String oauthToken); private static native int nativeGetOsmChangesetsCount(String oauthToken);
@WorkerThread @WorkerThread
public static int getOsmChangesetsCount(@NonNull Context context, @NonNull FragmentManager fm) public static int getOsmChangesetsCount(@NonNull FragmentManager fm)
{ {
final int[] editsCount = {-1}; final int[] editsCount = {-1};
NetworkPolicy.checkNetworkPolicy(fm, policy -> { NetworkPolicy.checkNetworkPolicy(fm, policy -> {
if (!policy.canUseNetwork()) if (!policy.canUseNetwork())
return; return;
final String token = getAuthToken(context); final String token = getAuthToken();
editsCount[0] = OsmOAuth.nativeGetOsmChangesetsCount(token); editsCount[0] = OsmOAuth.nativeGetOsmChangesetsCount(token);
}); });
final SharedPreferences prefs = MwmApplication.prefs(context);
if (editsCount[0] < 0) if (editsCount[0] < 0)
return prefs.getInt(PREF_OSM_CHANGESETS_COUNT, 0); return mPrefs.getInt(PREF_OSM_CHANGESETS_COUNT, 0);
prefs.edit().putInt(PREF_OSM_CHANGESETS_COUNT, editsCount[0]).apply(); mPrefs.edit().putInt(PREF_OSM_CHANGESETS_COUNT, editsCount[0]).apply();
return editsCount[0]; return editsCount[0];
} }
} }

View File

@@ -6,12 +6,15 @@ import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager; import androidx.preference.PreferenceManager;
import app.organicmaps.BuildConfig; import app.organicmaps.BuildConfig;
import app.organicmaps.MwmApplication;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.util.ThemeUtils; import app.organicmaps.util.ThemeUtils;
public final class Config public final class Config
{ {
@SuppressWarnings("NotNullFieldNotInitialized")
@NonNull
private static SharedPreferences mPrefs;
private static final String KEY_APP_STORAGE = "StoragePath"; private static final String KEY_APP_STORAGE = "StoragePath";
private static final String KEY_DOWNLOADER_AUTO = "AutoDownloadEnabled"; private static final String KEY_DOWNLOADER_AUTO = "AutoDownloadEnabled";
@@ -332,19 +335,19 @@ public final class Config
return url; return url;
} }
public static void init(@NonNull Context context) public static void init(@NonNull Context context, @NonNull SharedPreferences prefs)
{ {
PreferenceManager.setDefaultValues(context, R.xml.prefs_main, false); PreferenceManager.setDefaultValues(context, R.xml.prefs_main, false);
final SharedPreferences prefs = MwmApplication.prefs(context); mPrefs = prefs;
final SharedPreferences.Editor editor = prefs.edit(); final SharedPreferences.Editor editor = mPrefs.edit();
// Update counters. // Update counters.
final int launchNumber = prefs.getInt(KEY_APP_LAUNCH_NUMBER, 0); final int launchNumber = mPrefs.getInt(KEY_APP_LAUNCH_NUMBER, 0);
editor.putInt(KEY_APP_LAUNCH_NUMBER, launchNumber + 1); editor.putInt(KEY_APP_LAUNCH_NUMBER, launchNumber + 1);
editor.putLong(KEY_APP_LAST_SESSION_TIMESTAMP, System.currentTimeMillis()); editor.putLong(KEY_APP_LAST_SESSION_TIMESTAMP, System.currentTimeMillis());
editor.putInt(KEY_APP_LAST_INSTALL_VERSION_CODE, BuildConfig.VERSION_CODE); editor.putInt(KEY_APP_LAST_INSTALL_VERSION_CODE, BuildConfig.VERSION_CODE);
if (launchNumber == 0 || prefs.getInt(KEY_APP_FIRST_INSTALL_VERSION_CODE, 0) == 0) if (launchNumber == 0 || mPrefs.getInt(KEY_APP_FIRST_INSTALL_VERSION_CODE, 0) == 0)
editor.putInt(KEY_APP_FIRST_INSTALL_VERSION_CODE, BuildConfig.VERSION_CODE); editor.putInt(KEY_APP_FIRST_INSTALL_VERSION_CODE, BuildConfig.VERSION_CODE);
// Clean up legacy counters. // Clean up legacy counters.
@@ -367,12 +370,12 @@ public final class Config
public static boolean isFirstLaunch(@NonNull Context context) public static boolean isFirstLaunch(@NonNull Context context)
{ {
return !MwmApplication.prefs(context).getBoolean(KEY_MISC_FIRST_START_DIALOG_SEEN, false); return !mPrefs.getBoolean(KEY_MISC_FIRST_START_DIALOG_SEEN, false);
} }
public static void setFirstStartDialogSeen(@NonNull Context context) public static void setFirstStartDialogSeen(@NonNull Context context)
{ {
MwmApplication.prefs(context).edit().putBoolean(KEY_MISC_FIRST_START_DIALOG_SEEN, true).apply(); mPrefs.edit().putBoolean(KEY_MISC_FIRST_START_DIALOG_SEEN, true).apply();
} }
public static boolean isSearchHistoryEnabled() public static boolean isSearchHistoryEnabled()

View File

@@ -5,8 +5,6 @@ import static app.organicmaps.sdk.util.Config.KEY_PREF_STATISTICS;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import app.organicmaps.MwmApplication;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.maplayer.Mode; import app.organicmaps.sdk.maplayer.Mode;
import java.io.IOException; import java.io.IOException;
@@ -17,27 +15,34 @@ public final class SharedPropertiesUtils
private static final String PREFS_SHOW_EMULATE_BAD_STORAGE_SETTING = "ShowEmulateBadStorageSetting"; private static final String PREFS_SHOW_EMULATE_BAD_STORAGE_SETTING = "ShowEmulateBadStorageSetting";
private static final String PREFS_SHOULD_SHOW_LAYER_MARKER_FOR = "ShouldShowGuidesLayerMarkerFor"; private static final String PREFS_SHOULD_SHOW_LAYER_MARKER_FOR = "ShouldShowGuidesLayerMarkerFor";
@SuppressWarnings("NotNullFieldNotInitialized")
@NonNull
private static SharedPreferences mPrefs;
// Utils class // Utils class
private SharedPropertiesUtils() private SharedPropertiesUtils()
{ {
throw new IllegalStateException("Try instantiate utility class SharedPropertiesUtils"); throw new IllegalStateException("Try instantiate utility class SharedPropertiesUtils");
} }
public static boolean isStatisticsEnabled(@NonNull Context context) public static void init(@NonNull SharedPreferences prefs)
{ {
return MwmApplication.prefs(context).getBoolean(KEY_PREF_STATISTICS, true); mPrefs = prefs;
} }
public static void setShouldShowEmulateBadStorageSetting(@NonNull Context context, boolean show) public static boolean isStatisticsEnabled()
{ {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MwmApplication.from(context)); return mPrefs.getBoolean(KEY_PREF_STATISTICS, true);
prefs.edit().putBoolean(PREFS_SHOW_EMULATE_BAD_STORAGE_SETTING, show).apply();
} }
public static boolean shouldShowEmulateBadStorageSetting(@NonNull Context context) public static void setShouldShowEmulateBadStorageSetting(boolean show)
{ {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MwmApplication.from(context)); mPrefs.edit().putBoolean(PREFS_SHOW_EMULATE_BAD_STORAGE_SETTING, show).apply();
return prefs.getBoolean(PREFS_SHOW_EMULATE_BAD_STORAGE_SETTING, false); }
public static boolean shouldShowEmulateBadStorageSetting()
{
return mPrefs.getBoolean(PREFS_SHOW_EMULATE_BAD_STORAGE_SETTING, false);
} }
/** /**
@@ -46,38 +51,28 @@ public final class SharedPropertiesUtils
*/ */
public static void emulateBadExternalStorage(@NonNull Context context) throws IOException public static void emulateBadExternalStorage(@NonNull Context context) throws IOException
{ {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MwmApplication.from(context)); final String key = context.getString(R.string.pref_emulate_bad_external_storage);
String key = MwmApplication.from(context).getString(R.string.pref_emulate_bad_external_storage); if (mPrefs.getBoolean(key, false))
if (prefs.getBoolean(key, false))
{ {
// Emulate one time only -> reset setting to run normally next time. // Emulate one time only -> reset setting to run normally next time.
prefs.edit().putBoolean(key, false).apply(); mPrefs.edit().putBoolean(key, false).apply();
throw new IOException("Bad external storage error injection"); throw new IOException("Bad external storage error injection");
} }
} }
public static boolean shouldShowNewMarkerForLayerMode(@NonNull Context context, @NonNull Mode mode) public static boolean shouldShowNewMarkerForLayerMode(@NonNull Mode mode)
{ {
return switch (mode) return switch (mode)
{ {
case SUBWAY, TRAFFIC, ISOLINES -> false; case SUBWAY, TRAFFIC, ISOLINES -> false;
default -> default -> mPrefs.getBoolean(PREFS_SHOULD_SHOW_LAYER_MARKER_FOR + mode.name().toLowerCase(Locale.ENGLISH), true);
getBoolean(context, PREFS_SHOULD_SHOW_LAYER_MARKER_FOR + mode.name().toLowerCase(Locale.ENGLISH), true);
}; };
} }
public static void setLayerMarkerShownForLayerMode(@NonNull Context context, @NonNull Mode mode) public static void setLayerMarkerShownForLayerMode(@NonNull Mode mode)
{ {
putBoolean(context, PREFS_SHOULD_SHOW_LAYER_MARKER_FOR + mode.name().toLowerCase(Locale.ENGLISH), false); mPrefs.edit()
} .putBoolean(PREFS_SHOULD_SHOW_LAYER_MARKER_FOR + mode.name().toLowerCase(Locale.ENGLISH), false)
.apply();
private static boolean getBoolean(@NonNull Context context, @NonNull String key, boolean defValue)
{
return MwmApplication.prefs(context).getBoolean(key, defValue);
}
private static void putBoolean(@NonNull Context context, @NonNull String key, boolean value)
{
MwmApplication.prefs(context).edit().putBoolean(key, value).apply();
} }
} }

View File

@@ -2,7 +2,6 @@ package app.organicmaps.sdk.util.log;
import android.Manifest; import android.Manifest;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.app.Application;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
@@ -18,7 +17,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import app.organicmaps.BuildConfig; import app.organicmaps.BuildConfig;
import app.organicmaps.MwmApplication;
import app.organicmaps.R; import app.organicmaps.R;
import app.organicmaps.sdk.util.ROMUtils; import app.organicmaps.sdk.util.ROMUtils;
import app.organicmaps.sdk.util.StringUtils; import app.organicmaps.sdk.util.StringUtils;
@@ -34,7 +32,7 @@ import net.jcip.annotations.ThreadSafe;
/** /**
* By default uses Android's system logger. * By default uses Android's system logger.
* After an initFileLogging() call can use a custom file logging implementation. * After an initFileLogging() call can use a custom file logging implementation.
* * <p>
* Its important to have only system logging here to avoid infinite loop * Its important to have only system logging here to avoid infinite loop
* (Logger calls getEnabledLogsFolder() in preparation to write). * (Logger calls getEnabledLogsFolder() in preparation to write).
*/ */
@@ -44,7 +42,7 @@ public final class LogsManager
public interface OnZipCompletedListener public interface OnZipCompletedListener
{ {
// Called from the logger thread. // Called from the logger thread.
public void onCompleted(final boolean success, @Nullable final String zipPath); void onCompleted(final boolean success, @Nullable final String zipPath);
} }
private final static String TAG = LogsManager.class.getSimpleName(); private final static String TAG = LogsManager.class.getSimpleName();
@@ -53,7 +51,9 @@ public final class LogsManager
final static ExecutorService EXECUTOR = Executors.newSingleThreadExecutor(); final static ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
@Nullable @Nullable
private Application mApplication; private Context mApplicationContext;
@Nullable
private SharedPreferences mPrefs;
private boolean mIsFileLoggingEnabled = false; private boolean mIsFileLoggingEnabled = false;
@Nullable @Nullable
private String mLogsFolder; private String mLogsFolder;
@@ -63,13 +63,13 @@ public final class LogsManager
Log.i(LogsManager.TAG, "Logging started"); Log.i(LogsManager.TAG, "Logging started");
} }
public synchronized void initFileLogging(@NonNull Application application) public synchronized void initFileLogging(@NonNull Context context, @NonNull SharedPreferences prefs)
{ {
Log.i(TAG, "Init file logging"); Log.i(TAG, "Init file logging");
mApplication = application; mApplicationContext = context.getApplicationContext();
mPrefs = prefs;
final SharedPreferences prefs = MwmApplication.prefs(mApplication); mIsFileLoggingEnabled = mPrefs.getBoolean(mApplicationContext.getString(R.string.pref_enable_logging), false);
mIsFileLoggingEnabled = prefs.getBoolean(mApplication.getString(R.string.pref_enable_logging), false);
Log.i(TAG, "isFileLoggingEnabled preference: " + mIsFileLoggingEnabled); Log.i(TAG, "isFileLoggingEnabled preference: " + mIsFileLoggingEnabled);
mIsFileLoggingEnabled = mIsFileLoggingEnabled && ensureLogsFolder() != null; mIsFileLoggingEnabled = mIsFileLoggingEnabled && ensureLogsFolder() != null;
@@ -79,7 +79,7 @@ public final class LogsManager
private void assertFileLoggingInit() private void assertFileLoggingInit()
{ {
assert mApplication != null : "mApplication must be initialized first by calling initFileLogging()"; assert mApplicationContext != null : "mApplicationContext must be initialized first by calling initFileLogging()";
} }
/** /**
@@ -114,9 +114,9 @@ public final class LogsManager
if (mLogsFolder != null && createWritableDir(mLogsFolder)) if (mLogsFolder != null && createWritableDir(mLogsFolder))
return mLogsFolder; return mLogsFolder;
mLogsFolder = createLogsFolder(mApplication.getExternalFilesDir(null)); mLogsFolder = createLogsFolder(mApplicationContext.getExternalFilesDir(null));
if (mLogsFolder == null) if (mLogsFolder == null)
mLogsFolder = createLogsFolder(mApplication.getFilesDir()); mLogsFolder = createLogsFolder(mApplicationContext.getFilesDir());
if (mLogsFolder == null) if (mLogsFolder == null)
Log.e(TAG, "Can't create any logs folder"); Log.e(TAG, "Can't create any logs folder");
@@ -166,10 +166,7 @@ public final class LogsManager
mIsFileLoggingEnabled = enabled; mIsFileLoggingEnabled = enabled;
// Only Debug builds log DEBUG level to Android system log. // Only Debug builds log DEBUG level to Android system log.
nativeToggleCoreDebugLogs(enabled || BuildConfig.DEBUG); nativeToggleCoreDebugLogs(enabled || BuildConfig.DEBUG);
MwmApplication.prefs(mApplication) mPrefs.edit().putBoolean(mApplicationContext.getString(R.string.pref_enable_logging), enabled).apply();
.edit()
.putBoolean(mApplication.getString(R.string.pref_enable_logging), enabled)
.apply();
Log.i(TAG, "Logging to " + (enabled ? "logs folder " + mLogsFolder : "system log")); Log.i(TAG, "Logging to " + (enabled ? "logs folder " + mLogsFolder : "system log"));
} }
@@ -180,7 +177,7 @@ public final class LogsManager
/** /**
* Returns false if file logging can't be enabled. * Returns false if file logging can't be enabled.
* * <p>
* NOTE: initFileLogging() must be called before. * NOTE: initFileLogging() must be called before.
*/ */
public synchronized boolean setFileLoggingEnabled(boolean enabled) public synchronized boolean setFileLoggingEnabled(boolean enabled)
@@ -256,7 +253,7 @@ public final class LogsManager
.append(Locale.getDefault()) .append(Locale.getDefault())
.append("\nNetworks: "); .append("\nNetworks: ");
final ConnectivityManager manager = final ConnectivityManager manager =
(ConnectivityManager) mApplication.getSystemService(Context.CONNECTIVITY_SERVICE); (ConnectivityManager) mApplicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
if (manager != null) if (manager != null)
{ {
for (Network network : manager.getAllNetworks()) for (Network network : manager.getAllNetworks())
@@ -267,16 +264,16 @@ public final class LogsManager
} }
sb.append("\nLocation providers:"); sb.append("\nLocation providers:");
final LocationManager locMngr = final LocationManager locMngr =
(android.location.LocationManager) mApplication.getSystemService(Context.LOCATION_SERVICE); (android.location.LocationManager) mApplicationContext.getSystemService(Context.LOCATION_SERVICE);
if (locMngr != null) if (locMngr != null)
for (String provider : locMngr.getProviders(true)) for (String provider : locMngr.getProviders(true))
sb.append(' ').append(provider); sb.append(' ').append(provider);
sb.append("\nLocation permissions:"); sb.append("\nLocation permissions:");
if (ContextCompat.checkSelfPermission(mApplication, Manifest.permission.ACCESS_COARSE_LOCATION) if (ContextCompat.checkSelfPermission(mApplicationContext, Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED) == PackageManager.PERMISSION_GRANTED)
sb.append(' ').append("coarse"); sb.append(' ').append("coarse");
if (ContextCompat.checkSelfPermission(mApplication, Manifest.permission.ACCESS_FINE_LOCATION) if (ContextCompat.checkSelfPermission(mApplicationContext, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) == PackageManager.PERMISSION_GRANTED)
sb.append(' ').append("fine"); sb.append(' ').append("fine");

View File

@@ -586,7 +586,7 @@ public class SearchFragment extends BaseMwmFragment implements SearchListener, C
@Override @Override
void executeInternal() void executeInternal()
{ {
SharedPropertiesUtils.setShouldShowEmulateBadStorageSetting(mContext, true); SharedPropertiesUtils.setShouldShowEmulateBadStorageSetting(true);
} }
} }

View File

@@ -148,9 +148,9 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
private void updateProfileSettingsPrefsSummary() private void updateProfileSettingsPrefsSummary()
{ {
final Preference pref = getPreference(getString(R.string.pref_osm_profile)); final Preference pref = getPreference(getString(R.string.pref_osm_profile));
if (OsmOAuth.isAuthorized(requireContext())) if (OsmOAuth.isAuthorized())
{ {
final String username = OsmOAuth.getUsername(requireContext()); final String username = OsmOAuth.getUsername();
pref.setSummary(username); pref.setSummary(username);
} }
else else
@@ -285,7 +285,7 @@ public class SettingsPrefsFragment extends BaseXmlSettingsFragment implements La
if (pref == null) if (pref == null)
return; return;
if (!SharedPropertiesUtils.shouldShowEmulateBadStorageSetting(requireContext())) if (!SharedPropertiesUtils.shouldShowEmulateBadStorageSetting())
removePreference(getString(R.string.pref_settings_general), pref); removePreference(getString(R.string.pref_settings_general), pref);
else else
pref.setVisible(true); pref.setVisible(true);

View File

@@ -13,7 +13,6 @@
<string name="pref_enable_logging" translatable="false">EnableLogging</string> <string name="pref_enable_logging" translatable="false">EnableLogging</string>
<string name="pref_emulate_bad_external_storage" translatable="false">EmulateBadExternalStorage</string> <string name="pref_emulate_bad_external_storage" translatable="false">EmulateBadExternalStorage</string>
<string name="pref_about" translatable="false">AboutOrganicMaps</string> <string name="pref_about" translatable="false">AboutOrganicMaps</string>
<string name="pref_file_name" translatable="false">OrganicMapsPrefs</string>
<string name="pref_map_style" translatable="false">MapStyle</string> <string name="pref_map_style" translatable="false">MapStyle</string>
<string name="pref_tts_screen" translatable="false">TtsScreen</string> <string name="pref_tts_screen" translatable="false">TtsScreen</string>
<string name="pref_tts_enabled" translatable="false">TtsEnabled</string> <string name="pref_tts_enabled" translatable="false">TtsEnabled</string>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_file_name" translatable="false">OrganicMapsPrefs</string>
</resources>