diff --git a/OsmAnd/AndroidManifest.xml b/OsmAnd/AndroidManifest.xml index d762e0a261..f108fc7dd5 100644 --- a/OsmAnd/AndroidManifest.xml +++ b/OsmAnd/AndroidManifest.xml @@ -247,6 +247,7 @@ + @@ -262,6 +263,7 @@ + @@ -466,6 +468,13 @@ + + + + + + + @@ -478,28 +487,11 @@ - - - - - - - - - - - - - - - diff --git a/OsmAnd/res/layout/plugin.xml b/OsmAnd/res/layout/plugin.xml index b0436d95e5..056fc218f0 100644 --- a/OsmAnd/res/layout/plugin.xml +++ b/OsmAnd/res/layout/plugin.xml @@ -1,186 +1,204 @@ - + - + - + + + + + + + android:layout_height="wrap_content" + android:orientation="vertical" > - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:layout_marginStart="@dimen/content_padding" + android:layout_marginLeft="@dimen/content_padding" + android:layout_marginTop="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginRight="@dimen/content_padding" + android:text="@string/shared_string_description" + android:textColor="?android:textColorSecondary" + android:textSize="@dimen/default_desc_text_size" + osmand:textAllCapsCompat="true" + osmand:typeface="@string/font_roboto_medium" /> - + - + - + - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/OsmAnd/res/layout/plugins.xml b/OsmAnd/res/layout/plugins.xml index 095bfee61c..66d220fce8 100644 --- a/OsmAnd/res/layout/plugins.xml +++ b/OsmAnd/res/layout/plugins.xml @@ -1,14 +1,24 @@ - + + + + + + - + android:dividerHeight="1dp" + android:drawSelectorOnTop="true" /> + + \ No newline at end of file diff --git a/OsmAnd/res/layout/plugins_list_item.xml b/OsmAnd/res/layout/plugins_list_item.xml index c616b0014f..4e8f369e0c 100644 --- a/OsmAnd/res/layout/plugins_list_item.xml +++ b/OsmAnd/res/layout/plugins_list_item.xml @@ -52,6 +52,7 @@ android:ellipsize="end" android:lines="2" android:maxLines="2" + android:scrollbars="none" android:text="@string/lorem_ipsum" android:textColor="?android:textColorSecondary" android:textSize="@dimen/default_desc_text_size" diff --git a/OsmAnd/res/layout/profile_preference_toolbar.xml b/OsmAnd/res/layout/profile_preference_toolbar.xml index b76eb61696..6776a7a70c 100644 --- a/OsmAnd/res/layout/profile_preference_toolbar.xml +++ b/OsmAnd/res/layout/profile_preference_toolbar.xml @@ -64,6 +64,13 @@ tools:text="Some description" /> + + \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/access/AccessibilityPlugin.java b/OsmAnd/src/net/osmand/access/AccessibilityPlugin.java index b9ea4a62cf..eb806a5a33 100644 --- a/OsmAnd/src/net/osmand/access/AccessibilityPlugin.java +++ b/OsmAnd/src/net/osmand/access/AccessibilityPlugin.java @@ -8,9 +8,9 @@ import androidx.annotation.NonNull; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import java.io.IOException; import java.util.HashMap; @@ -66,13 +66,8 @@ public class AccessibilityPlugin extends OsmandPlugin { } @Override - public Class getSettingsActivity() { - return SettingsAccessibilityActivity.class; - } - - @Override - public Class getSettingsFragment() { - return AccessibilitySettingsFragment.class; + public SettingsScreenType getSettingsScreenType() { + return SettingsScreenType.ACCESSIBILITY_SETTINGS; } @Override diff --git a/OsmAnd/src/net/osmand/access/AccessibilitySettingsFragment.java b/OsmAnd/src/net/osmand/access/AccessibilitySettingsFragment.java index 781a10bf41..609c43fcff 100644 --- a/OsmAnd/src/net/osmand/access/AccessibilitySettingsFragment.java +++ b/OsmAnd/src/net/osmand/access/AccessibilitySettingsFragment.java @@ -4,6 +4,8 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.provider.Settings; +import android.view.LayoutInflater; +import android.view.View; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; import android.widget.ImageView; @@ -13,21 +15,24 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceViewHolder; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.access.AccessibilityMode; import net.osmand.plus.access.RelativeDirectionStyle; +import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; import net.osmand.plus.profiles.SelectCopyAppModeBottomSheet; import net.osmand.plus.profiles.SelectCopyAppModeBottomSheet.CopyAppModePrefsListener; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; -import net.osmand.plus.settings.fragments.OnPreferenceChanged; +import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.bottomsheets.ResetProfilePrefsBottomSheet; import net.osmand.plus.settings.bottomsheets.ResetProfilePrefsBottomSheet.ResetAppModePrefsListener; +import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.fragments.OnPreferenceChanged; import net.osmand.plus.settings.preferences.ListPreferenceEx; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; +import static net.osmand.plus.activities.PluginInfoFragment.PLUGIN_INFO; + public class AccessibilitySettingsFragment extends BaseSettingsFragment implements OnPreferenceChanged, CopyAppModePrefsListener, ResetAppModePrefsListener { private static final String ACCESSIBILITY_OPTIONS = "accessibility_options"; @@ -36,6 +41,8 @@ public class AccessibilitySettingsFragment extends BaseSettingsFragment implemen private AccessibilityStateChangeListener accessibilityListener; + boolean showSwitchProfile = false; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -47,6 +54,28 @@ public class AccessibilitySettingsFragment extends BaseSettingsFragment implemen } } }; + + Bundle args = getArguments(); + if (args != null) { + showSwitchProfile = args.getBoolean(PLUGIN_INFO, false); + } + } + + @Override + protected void createToolbar(LayoutInflater inflater, View view) { + super.createToolbar(inflater, view); + + View switchProfile = view.findViewById(R.id.profile_button); + if (switchProfile != null) { + AndroidUiHelper.updateVisibility(switchProfile, showSwitchProfile); + } + } + + @Override + public Bundle buildArguments() { + Bundle args = super.buildArguments(); + args.putBoolean(PLUGIN_INFO, showSwitchProfile); + return args; } @Override diff --git a/OsmAnd/src/net/osmand/access/SettingsAccessibilityActivity.java b/OsmAnd/src/net/osmand/access/SettingsAccessibilityActivity.java deleted file mode 100644 index 719ed05863..0000000000 --- a/OsmAnd/src/net/osmand/access/SettingsAccessibilityActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -package net.osmand.access; - - -import android.os.Bundle; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceCategory; -import android.preference.PreferenceGroup; -import android.preference.PreferenceScreen; - -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.R; -import net.osmand.plus.access.AccessibilityMode; -import net.osmand.plus.access.RelativeDirectionStyle; -import net.osmand.plus.activities.SettingsBaseActivity; - -public class SettingsAccessibilityActivity extends SettingsBaseActivity { - - private ListPreference accessibilityModePreference; - private ListPreference directionStylePreference; - private ListPreference autoannouncePeriodPreference; - - - @Override - public void onCreate(Bundle savedInstanceState) { - ((OsmandApplication) getApplication()).applyTheme(this); - super.onCreate(savedInstanceState); - getToolbar().setTitle(R.string.shared_string_accessibility); - PreferenceScreen grp = getPreferenceScreen(); - - String[] entries = new String[AccessibilityMode.values().length]; - for (int i = 0; i < entries.length; i++) { - entries[i] = AccessibilityMode.values()[i].toHumanString(getMyApplication()); - } - accessibilityModePreference = createListPreference(settings.ACCESSIBILITY_MODE, entries, AccessibilityMode.values(), - R.string.accessibility_mode, R.string.accessibility_mode_descr); - accessibilityModePreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - private final OnPreferenceChangeListener committer = accessibilityModePreference.getOnPreferenceChangeListener(); - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (committer != null) - committer.onPreferenceChange(preference, newValue); - updateAllSettings(); - return true; - } - }); - addSpeechRateSetting(grp); - - grp.addPreference(accessibilityModePreference); - PreferenceCategory cat = new PreferenceCategory(this); - cat.setKey("accessibility_options"); - cat.setTitle(R.string.accessibility_options); - cat.setEnabled(getMyApplication().accessibilityEnabled()); - grp.addPreference(cat); - - entries = new String[RelativeDirectionStyle.values().length]; - for (int i = 0; i < entries.length; i++) { - entries[i] = RelativeDirectionStyle.values()[i].toHumanString(getMyApplication()); - } - directionStylePreference = createListPreference(settings.DIRECTION_STYLE, entries, RelativeDirectionStyle.values(), - R.string.settings_direction_style, R.string.settings_direction_style_descr); - directionStylePreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - private final OnPreferenceChangeListener committer = directionStylePreference.getOnPreferenceChangeListener(); - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (committer != null) - committer.onPreferenceChange(preference, newValue); - updateAllSettings(); - return true; - } - }); - cat.addPreference(directionStylePreference); - - cat.addPreference(createCheckBoxPreference(settings.ACCESSIBILITY_SMART_AUTOANNOUNCE, R.string.access_smart_autoannounce, - R.string.access_smart_autoannounce_descr)); - - final int[] seconds = new int[] {5, 10, 15, 20, 30, 45, 60, 90}; - final int[] minutes = new int[] {2, 3, 5}; - autoannouncePeriodPreference = createTimeListPreference(settings.ACCESSIBILITY_AUTOANNOUNCE_PERIOD, seconds, minutes, 1000, - R.string.access_autoannounce_period, R.string.access_autoannounce_period_descr); - autoannouncePeriodPreference.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - private final OnPreferenceChangeListener committer = autoannouncePeriodPreference.getOnPreferenceChangeListener(); - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (committer != null) - committer.onPreferenceChange(preference, newValue); - updateAllSettings(); - return true; - } - }); - cat.addPreference(autoannouncePeriodPreference); - cat.addPreference(createCheckBoxPreference(settings.DIRECTION_AUDIO_FEEDBACK, R.string.access_direction_audio_feedback, - R.string.access_direction_audio_feedback_descr)); - cat.addPreference(createCheckBoxPreference(settings.DIRECTION_HAPTIC_FEEDBACK, R.string.access_direction_haptic_feedback, - R.string.access_direction_haptic_feedback_descr)); - - } - - - protected void addSpeechRateSetting(PreferenceGroup grp) { - Float[] sprValues = new Float[] {0.5f, 0.75f, 1f, 1.25f, 1.5f, 2f} ; - String[] sprNames = new String[sprValues.length]; - for(int i = 0; i < sprNames.length; i++) { - sprNames[i] = (int)(sprValues[i] * 100) + " %"; - } - grp.addPreference(createListPreference(settings.SPEECH_RATE, sprNames, sprValues, R.string.speech_rate, R.string.speech_rate_descr)); - } - - - - public void updateAllSettings() { - super.updateAllSettings(); - PreferenceCategory accessibilityOptions = ((PreferenceCategory)(getPreferenceScreen().findPreference("accessibility_options"))); - if (accessibilityOptions != null) - accessibilityOptions.setEnabled(getMyApplication().accessibilityEnabled()); - if(accessibilityModePreference != null) { - accessibilityModePreference.setSummary(getString(R.string.accessibility_mode_descr) + " [" + settings.ACCESSIBILITY_MODE.get().toHumanString(getMyApplication()) + "]"); - } - if(directionStylePreference != null) { - directionStylePreference.setSummary(getString(R.string.settings_direction_style_descr) + " [" + settings.DIRECTION_STYLE.get().toHumanString(getMyApplication()) + "]"); - } - if(autoannouncePeriodPreference != null) { - autoannouncePeriodPreference.setSummary(getString(R.string.access_autoannounce_period_descr) + " [" + autoannouncePeriodPreference.getEntry() + "]"); - } - } - -} diff --git a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java index bad1f3998e..21ed74a437 100644 --- a/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java +++ b/OsmAnd/src/net/osmand/aidl/OsmandAidlApi.java @@ -81,8 +81,10 @@ import net.osmand.plus.routing.VoiceRouter; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.SettingsHelper; +import net.osmand.plus.settings.backend.backup.ProfileSettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsHelper; import net.osmand.plus.settings.backend.ExportSettingsType; +import net.osmand.plus.settings.backend.backup.SettingsItem; import net.osmand.plus.views.OsmandMapLayer; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.layers.AidlMapLayer; @@ -138,7 +140,7 @@ import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_DIRECTION_NAME; import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_DIRECTION_TURN; import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_DISTANCE; import static net.osmand.plus.helpers.ExternalApiHelper.PARAM_NT_IMMINENT; -import static net.osmand.plus.settings.backend.SettingsHelper.REPLACE_KEY; +import static net.osmand.plus.settings.backend.backup.SettingsHelper.REPLACE_KEY; public class OsmandAidlApi { @@ -2305,8 +2307,8 @@ public class OsmandAidlApi { for (String key : settingsTypesKeys) { settingsTypes.add(ExportSettingsType.valueOf(key)); } - List settingsItems = new ArrayList<>(); - settingsItems.add(new SettingsHelper.ProfileSettingsItem(app, appMode)); + List settingsItems = new ArrayList<>(); + settingsItems.add(new ProfileSettingsItem(app, appMode)); File exportDir = app.getSettings().getExternalStorageDirectory(); String fileName = appMode.toHumanString(); SettingsHelper settingsHelper = app.getSettingsHelper(); diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index 78364460a7..58690a9a3b 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -55,7 +55,7 @@ import net.osmand.plus.routing.TransportRoutingHelper; import net.osmand.plus.search.QuickSearchHelper; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.SettingsHelper; +import net.osmand.plus.settings.backend.backup.SettingsHelper; import net.osmand.plus.views.corenative.NativeCoreContext; import net.osmand.plus.voice.CommandPlayer; import net.osmand.plus.voice.CommandPlayerException; diff --git a/OsmAnd/src/net/osmand/plus/CustomOsmandPlugin.java b/OsmAnd/src/net/osmand/plus/CustomOsmandPlugin.java index e0cb743a64..9127fa0aef 100644 --- a/OsmAnd/src/net/osmand/plus/CustomOsmandPlugin.java +++ b/OsmAnd/src/net/osmand/plus/CustomOsmandPlugin.java @@ -18,15 +18,15 @@ import net.osmand.data.LatLon; import net.osmand.map.ITileSource; import net.osmand.map.WorldRegion; import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.SettingsHelper; -import net.osmand.plus.settings.backend.SettingsHelper.AvoidRoadsSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.MapSourcesSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.PluginSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.PoiUiFiltersSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.ProfileSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.QuickActionsSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsCollectListener; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsHelper; +import net.osmand.plus.settings.backend.backup.AvoidRoadsSettingsItem; +import net.osmand.plus.settings.backend.backup.MapSourcesSettingsItem; +import net.osmand.plus.settings.backend.backup.PluginSettingsItem; +import net.osmand.plus.settings.backend.backup.PoiUiFiltersSettingsItem; +import net.osmand.plus.settings.backend.backup.ProfileSettingsItem; +import net.osmand.plus.settings.backend.backup.QuickActionsSettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsHelper.SettingsCollectListener; +import net.osmand.plus.settings.backend.backup.SettingsItem; import net.osmand.plus.download.DownloadActivityType; import net.osmand.plus.download.DownloadIndexesThread; import net.osmand.plus.download.DownloadResources; diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java index 1a48650a4d..b57d94dc3a 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java +++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java @@ -75,7 +75,7 @@ import net.osmand.plus.search.QuickSearchHelper; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.SettingsHelper; +import net.osmand.plus.settings.backend.backup.SettingsHelper; import net.osmand.plus.voice.CommandPlayer; import net.osmand.plus.wikivoyage.data.TravelDbHelper; import net.osmand.router.GeneralRouter; diff --git a/OsmAnd/src/net/osmand/plus/OsmandPlugin.java b/OsmAnd/src/net/osmand/plus/OsmandPlugin.java index e86b64df43..6d353713a0 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandPlugin.java +++ b/OsmAnd/src/net/osmand/plus/OsmandPlugin.java @@ -48,7 +48,7 @@ import net.osmand.plus.search.QuickSearchDialogFragment; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.OsmandPreference; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import net.osmand.plus.skimapsplugin.SkiMapsPlugin; import net.osmand.plus.srtmplugin.SRTMPlugin; import net.osmand.plus.views.OsmandMapTileView; @@ -111,11 +111,7 @@ public abstract class OsmandPlugin { return app.getUIUtilities().getIcon(getLogoResourceId()); } - public Class getSettingsActivity() { - return null; - } - - public Class getSettingsFragment() { + public SettingsScreenType getSettingsScreenType() { return null; } diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java index 53d3fcf473..8cb6e832c5 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivity.java @@ -118,6 +118,7 @@ import net.osmand.plus.measurementtool.GpxData; import net.osmand.plus.measurementtool.MeasurementEditingContext; import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.measurementtool.SnapTrackWarningFragment; +import net.osmand.plus.osmedit.OsmEditingFragment; import net.osmand.plus.render.RendererRegistry; import net.osmand.plus.resources.ResourceManager; import net.osmand.plus.routepreparationmenu.ChooseRouteFragment; @@ -2216,6 +2217,10 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven return getFragment(GpxApproximationFragment.TAG); } + public OsmEditingFragment getOsmEditingFragment() { + return getFragment(SettingsScreenType.OPEN_STREET_MAP_EDITING.fragmentName); + } + public SnapTrackWarningFragment getSnapTrackWarningBottomSheet() { return getFragment(SnapTrackWarningFragment.TAG); } diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index d9137ee2d6..294b382ca4 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -974,10 +974,7 @@ public class MapActivityActions implements DialogProvider { @Override public boolean onContextMenuClick(ArrayAdapter adapter, int itemId, int pos, boolean isChecked, int[] viewCoordinates) { app.logEvent("drawer_plugins_open"); - Intent newIntent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization() - .getPluginsActivity()); - newIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - mapActivity.startActivity(newIntent); + PluginsFragment.showInstance(mapActivity.getSupportFragmentManager()); return true; } }).createItem()); diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginActivity.java b/OsmAnd/src/net/osmand/plus/activities/PluginActivity.java deleted file mode 100644 index e367f3cd8e..0000000000 --- a/OsmAnd/src/net/osmand/plus/activities/PluginActivity.java +++ /dev/null @@ -1,241 +0,0 @@ -package net.osmand.plus.activities; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Intent; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.text.method.LinkMovementMethod; -import android.util.Log; -import android.view.MenuItem; -import android.view.View; -import android.widget.Button; -import android.widget.CompoundButton; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; - -import net.osmand.AndroidUtils; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.R; -import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; -import net.osmand.plus.dialogs.PluginInstalledBottomSheetDialog; -import net.osmand.plus.download.DownloadIndexesThread; -import net.osmand.plus.srtmplugin.SRTMPlugin; - -public class PluginActivity extends OsmandActionBarActivity implements DownloadIndexesThread.DownloadEvents, PluginInstalledBottomSheetDialog.PluginStateListener { - private static final String TAG = "PluginActivity"; - public static final String EXTRA_PLUGIN_ID = "plugin_id"; - - private OsmandPlugin plugin; - - @Override - protected void onCreate(Bundle savedInstanceState) { - ((OsmandApplication) getApplication()).applyTheme(this); - super.onCreate(savedInstanceState); - - Intent intent = getIntent(); - if (intent == null || !intent.hasExtra(EXTRA_PLUGIN_ID)) { - Log.e(TAG, "Required extra '" + EXTRA_PLUGIN_ID + "' is missing"); - finish(); - return; - } - String pluginId = intent.getStringExtra(EXTRA_PLUGIN_ID); - if (pluginId == null) { - Log.e(TAG, "Extra '" + EXTRA_PLUGIN_ID + "' is null"); - finish(); - return; - } - for (OsmandPlugin plugin : OsmandPlugin.getAvailablePlugins()) { - if (!plugin.getId().equals(pluginId)) - continue; - - this.plugin = plugin; - break; - } - if (plugin == null) { - Log.e(TAG, "Plugin '" + EXTRA_PLUGIN_ID + "' not found"); - finish(); - return; - } - - setContentView(R.layout.plugin); - //noinspection ConstantConditions - getSupportActionBar().setTitle(plugin.getName()); - Drawable pluginImage = plugin.getAssetResourceImage(); - if (pluginImage != null) { - ImageView img = (ImageView) findViewById(R.id.plugin_image); - img.setImageDrawable(pluginImage); - } else { - findViewById(R.id.plugin_image_placeholder).setVisibility(View.VISIBLE); - } - - TextView descriptionView = (TextView) findViewById(R.id.plugin_description); - descriptionView.setText(plugin.getDescription()); - - boolean light = getMyApplication().getSettings().isLightContent(); - int linkTextColor = ContextCompat.getColor(this, - light ? R.color.ctx_menu_bottom_view_url_color_light : R.color.ctx_menu_bottom_view_url_color_dark); - - descriptionView.setLinkTextColor(linkTextColor); - descriptionView.setMovementMethod(LinkMovementMethod.getInstance()); - AndroidUtils.removeLinkUnderline(descriptionView); - - Button settingsButton = (Button) findViewById(R.id.plugin_settings); - settingsButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - startActivity(new Intent(PluginActivity.this, plugin.getSettingsActivity())); - } - }); - - CompoundButton enableDisableButton = (CompoundButton)findViewById( - R.id.plugin_enable_disable); - enableDisableButton.setOnCheckedChangeListener( - new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (plugin.isActive() == isChecked) { - return; - } - - boolean ok = OsmandPlugin.enablePlugin(PluginActivity.this, (OsmandApplication)getApplication(), - plugin, isChecked); - if (!ok) { - return; - } - updateState(); - } - }); - Button getButton = (Button)findViewById(R.id.plugin_get); - getButton.setText(plugin.isPaid() ? R.string.get_plugin : R.string.shared_string_install); - getButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - try { - if (plugin instanceof SRTMPlugin) { - FragmentManager fragmentManager = getSupportFragmentManager(); - if (fragmentManager != null) { - ChoosePlanDialogFragment.showHillshadeSrtmPluginInstance(fragmentManager); - } - } else { - startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(plugin.getInstallURL()))); - } - } catch (Exception e) { - //ignored - } - } - }); - - updateState(); - } - - @Override - protected void onResume() { - super.onResume(); - OsmandApplication app = getMyApplication(); - OsmandPlugin.checkInstalledMarketPlugins(app, this); - app.getDownloadThread().setUiActivity(this); - updateState(); - } - - @Override - protected void onPause() { - super.onPause(); - getMyApplication().getDownloadThread().resetUiActivity(this); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int itemId = item.getItemId(); - switch (itemId) { - case android.R.id.home: - finish(); - return true; - - } - return false; - } - - @SuppressLint("NewApi") - private void updateState() { - CompoundButton enableDisableButton = (CompoundButton)findViewById( - R.id.plugin_enable_disable); - Button getButton = (Button)findViewById(R.id.plugin_get); - Button settingsButton = (Button)findViewById(R.id.plugin_settings); - settingsButton.setCompoundDrawablesWithIntrinsicBounds( - getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_action_settings), - null, null, null); - View installHeader = findViewById(R.id.plugin_install_header); - - if (plugin.needsInstallation()) { - getButton.setVisibility(View.VISIBLE); - enableDisableButton.setVisibility(View.GONE); - settingsButton.setVisibility(View.GONE); - installHeader.setVisibility(View.VISIBLE); - View worldGlobeIcon = installHeader.findViewById(R.id.ic_world_globe); - Drawable worldGlobeDrawable = getMyApplication().getUIUtilities().getThemedIcon( - R.drawable.ic_world_globe_dark); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - worldGlobeIcon.setBackground(worldGlobeDrawable); - } else { - //noinspection deprecation - worldGlobeIcon.setBackgroundDrawable(worldGlobeDrawable); - } - } else { - getButton.setVisibility(View.GONE); - enableDisableButton.setVisibility(View.VISIBLE); - enableDisableButton.setChecked(plugin.isActive()); - - final Class settingsActivity = plugin.getSettingsActivity(); - if (settingsActivity == null || !plugin.isActive()) { - settingsButton.setVisibility(View.GONE); - } else { - settingsButton.setVisibility(View.VISIBLE); - } - - installHeader.setVisibility(View.GONE); - } - } - - // DownloadEvents - @Override - public void newDownloadIndexes() { - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - if (fragment instanceof DownloadIndexesThread.DownloadEvents && fragment.isAdded()) { - ((DownloadIndexesThread.DownloadEvents) fragment).newDownloadIndexes(); - } - } - } - - @Override - public void downloadInProgress() { - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - if (fragment instanceof DownloadIndexesThread.DownloadEvents && fragment.isAdded()) { - ((DownloadIndexesThread.DownloadEvents) fragment).downloadInProgress(); - } - } - } - - @Override - public void downloadHasFinished() { - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - if (fragment instanceof DownloadIndexesThread.DownloadEvents && fragment.isAdded()) { - ((DownloadIndexesThread.DownloadEvents) fragment).downloadHasFinished(); - } - } - } - - @Override - public void onPluginStateChanged(OsmandPlugin plugin) { - updateState(); - } -} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java b/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java new file mode 100644 index 0000000000..7deb85768c --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java @@ -0,0 +1,258 @@ +package net.osmand.plus.activities; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.CompoundButton; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.activity.OnBackPressedCallback; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; + +import net.osmand.AndroidUtils; +import net.osmand.PlatformUtil; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.base.BaseOsmAndFragment; +import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; +import net.osmand.plus.dialogs.PluginInstalledBottomSheetDialog.PluginStateListener; +import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; +import net.osmand.plus.srtmplugin.SRTMPlugin; + +import org.apache.commons.logging.Log; + +public class PluginInfoFragment extends BaseOsmAndFragment implements PluginStateListener { + + private static final Log log = PlatformUtil.getLog(PluginInfoFragment.class); + + private static final String TAG = PluginInfoFragment.class.getName(); + + public static final String EXTRA_PLUGIN_ID = "plugin_id"; + public static final String PLUGIN_INFO = "plugin_info"; + + private OsmandPlugin plugin; + private OsmandApplication app; + + private View mainView; + private boolean nightMode; + + @Override + public int getStatusBarColorId() { + return nightMode ? R.color.status_bar_color_dark : R.color.status_bar_color_light; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + FragmentActivity activity = requireMyActivity(); + activity.getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { + public void handleOnBackPressed() { + dismiss(); + } + }); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + app = requireMyApplication(); + + Bundle args = getArguments(); + if (args == null || !args.containsKey(EXTRA_PLUGIN_ID)) { + log.error("Required extra '" + EXTRA_PLUGIN_ID + "' is missing"); + return null; + } + String pluginId = args.getString(EXTRA_PLUGIN_ID); + if (pluginId == null) { + log.error("Extra '" + EXTRA_PLUGIN_ID + "' is null"); + return null; + } + plugin = OsmandPlugin.getPlugin(pluginId); + if (plugin == null) { + log.error("Plugin '" + EXTRA_PLUGIN_ID + "' not found"); + return null; + } + + Context context = requireContext(); + nightMode = !app.getSettings().isLightContent(); + LayoutInflater themedInflater = UiUtilities.getInflater(context, nightMode); + mainView = themedInflater.inflate(R.layout.plugin, container, false); + AndroidUtils.addStatusBarPadding21v(context, mainView); + + TextView toolbarTitle = mainView.findViewById(R.id.toolbar_title); + toolbarTitle.setText(plugin.getName()); + + ImageView closeButton = mainView.findViewById(R.id.close_button); + closeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Activity activity = getMyActivity(); + if (activity != null) { + activity.onBackPressed(); + } + } + }); + UiUtilities.rotateImageByLayoutDirection(closeButton, AndroidUtils.getLayoutDirection(app)); + + Drawable pluginImage = plugin.getAssetResourceImage(); + if (pluginImage != null) { + ImageView img = mainView.findViewById(R.id.plugin_image); + img.setImageDrawable(pluginImage); + } else { + mainView.findViewById(R.id.plugin_image_placeholder).setVisibility(View.VISIBLE); + } + + TextView descriptionView = mainView.findViewById(R.id.plugin_description); + descriptionView.setText(plugin.getDescription()); + + int linkTextColorId = nightMode ? R.color.ctx_menu_bottom_view_url_color_dark : R.color.ctx_menu_bottom_view_url_color_light; + int linkTextColor = ContextCompat.getColor(context, linkTextColorId); + + descriptionView.setLinkTextColor(linkTextColor); + descriptionView.setMovementMethod(LinkMovementMethod.getInstance()); + AndroidUtils.removeLinkUnderline(descriptionView); + + Button settingsButton = mainView.findViewById(R.id.plugin_settings); + settingsButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + FragmentActivity activity = getActivity(); + if (activity != null) { + SettingsScreenType settingsScreenType = plugin.getSettingsScreenType(); + if (settingsScreenType != null) { + Bundle args = new Bundle(); + args.putBoolean(PLUGIN_INFO, true); + BaseSettingsFragment.showInstance(activity, settingsScreenType, null, args); + } + } + } + }); + + CompoundButton enableDisableButton = mainView.findViewById(R.id.plugin_enable_disable); + enableDisableButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + if (plugin.isActive() == isChecked) { + return; + } + + boolean ok = OsmandPlugin.enablePlugin(getActivity(), app, plugin, isChecked); + if (!ok) { + return; + } + updateState(); + } + }); + Button getButton = mainView.findViewById(R.id.plugin_get); + getButton.setText(plugin.isPaid() ? R.string.get_plugin : R.string.shared_string_install); + getButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + try { + if (plugin instanceof SRTMPlugin) { + FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); + if (fragmentManager != null) { + ChoosePlanDialogFragment.showHillshadeSrtmPluginInstance(fragmentManager); + } + } else { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(plugin.getInstallURL()))); + } + } catch (Exception e) { + //ignored + } + } + }); + + updateState(); + return mainView; + } + + @Override + public void onResume() { + super.onResume(); + OsmandPlugin.checkInstalledMarketPlugins(app, getActivity()); + updateState(); + } + + private void updateState() { + CompoundButton enableDisableButton = mainView.findViewById(R.id.plugin_enable_disable); + Button getButton = mainView.findViewById(R.id.plugin_get); + Button settingsButton = mainView.findViewById(R.id.plugin_settings); + settingsButton.setCompoundDrawablesWithIntrinsicBounds(app.getUIUtilities().getThemedIcon(R.drawable.ic_action_settings), null, null, null); + View installHeader = mainView.findViewById(R.id.plugin_install_header); + + if (plugin.needsInstallation()) { + getButton.setVisibility(View.VISIBLE); + enableDisableButton.setVisibility(View.GONE); + settingsButton.setVisibility(View.GONE); + installHeader.setVisibility(View.VISIBLE); + View worldGlobeIcon = installHeader.findViewById(R.id.ic_world_globe); + Drawable worldGlobeDrawable = app.getUIUtilities().getThemedIcon(R.drawable.ic_world_globe_dark); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { + worldGlobeIcon.setBackground(worldGlobeDrawable); + } else { + worldGlobeIcon.setBackgroundDrawable(worldGlobeDrawable); + } + } else { + getButton.setVisibility(View.GONE); + enableDisableButton.setVisibility(View.VISIBLE); + enableDisableButton.setChecked(plugin.isActive()); + + if (plugin.getSettingsScreenType() == null || !plugin.isActive()) { + settingsButton.setVisibility(View.GONE); + } else { + settingsButton.setVisibility(View.VISIBLE); + } + installHeader.setVisibility(View.GONE); + } + } + + @Override + public void onPluginStateChanged(OsmandPlugin plugin) { + updateState(); + } + + public void dismiss() { + FragmentActivity activity = getActivity(); + if (activity != null) { + try { + activity.getSupportFragmentManager().popBackStack(TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE); + } catch (Exception e) { + log.error(e); + } + } + } + + public static boolean showInstance(FragmentManager fragmentManager, OsmandPlugin plugin) { + try { + Bundle args = new Bundle(); + args.putString(EXTRA_PLUGIN_ID, plugin.getId()); + + PluginInfoFragment fragment = new PluginInfoFragment(); + fragment.setArguments(args); + fragmentManager.beginTransaction() + .add(R.id.fragmentContainer, fragment, TAG) + .addToBackStack(TAG) + .commitAllowingStateLoss(); + return true; + } catch (Exception e) { + return false; + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginsActivity.java b/OsmAnd/src/net/osmand/plus/activities/PluginsActivity.java deleted file mode 100644 index a0e731c7bc..0000000000 --- a/OsmAnd/src/net/osmand/plus/activities/PluginsActivity.java +++ /dev/null @@ -1,296 +0,0 @@ -package net.osmand.plus.activities; - -import android.app.Activity; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.res.TypedArray; -import android.os.Bundle; -import android.text.method.LinkMovementMethod; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.widget.PopupMenu; -import androidx.core.content.ContextCompat; -import androidx.fragment.app.Fragment; - -import net.osmand.AndroidUtils; -import net.osmand.aidl.ConnectedApp; -import net.osmand.plus.CustomOsmandPlugin; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.R; -import net.osmand.plus.UiUtilities; -import net.osmand.plus.dialogs.PluginInstalledBottomSheetDialog; -import net.osmand.plus.download.DownloadIndexesThread; - -import java.util.ArrayList; - -public class PluginsActivity extends OsmandListActivity implements DownloadIndexesThread.DownloadEvents, PluginInstalledBottomSheetDialog.PluginStateListener { - - public static final int ACTIVE_PLUGINS_LIST_MODIFIED = 1; - - private boolean listModified = false; - - @Override - protected void onCreate(Bundle savedInstanceState) { - getMyApplication().applyTheme(this); - super.onCreate(savedInstanceState); - setContentView(R.layout.plugins); - getSupportActionBar().setTitle(R.string.plugins_screen); - setListAdapter(new PluginsListAdapter()); - } - - @Override - public PluginsListAdapter getListAdapter() { - return (PluginsListAdapter) super.getListAdapter(); - } - - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - Object tag = view.getTag(); - if (tag instanceof OsmandPlugin) { - Intent intent = new Intent(this, PluginActivity.class); - intent.putExtra(PluginActivity.EXTRA_PLUGIN_ID, ((OsmandPlugin) tag).getId()); - startActivity(intent); - } else if (tag instanceof ConnectedApp) { - switchEnabled((ConnectedApp) tag); - } - } - - @Override - protected void onResume() { - super.onResume(); - OsmandApplication app = getMyApplication(); - OsmandPlugin.checkInstalledMarketPlugins(app, this); - app.getDownloadThread().setUiActivity(this); - getListAdapter().notifyDataSetChanged(); - } - - @Override - protected void onPause() { - super.onPause(); - getMyApplication().getDownloadThread().resetUiActivity(this); - } - - private void enableDisablePlugin(OsmandPlugin plugin, boolean enable) { - OsmandApplication app = getMyApplication(); - if (OsmandPlugin.enablePlugin(this, app, plugin, enable)) { - if (!listModified) { - setResult(ACTIVE_PLUGINS_LIST_MODIFIED); - listModified = true; - } - getListAdapter().notifyDataSetChanged(); - } - } - - private void switchEnabled(@NonNull ConnectedApp app) { - getMyApplication().getAidlApi().switchEnabled(app); - getListAdapter().notifyDataSetChanged(); - } - - // DownloadEvents - @Override - public void newDownloadIndexes() { - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - if (fragment instanceof DownloadIndexesThread.DownloadEvents && fragment.isAdded()) { - ((DownloadIndexesThread.DownloadEvents) fragment).newDownloadIndexes(); - } - } - } - - @Override - public void downloadInProgress() { - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - if (fragment instanceof DownloadIndexesThread.DownloadEvents && fragment.isAdded()) { - ((DownloadIndexesThread.DownloadEvents) fragment).downloadInProgress(); - } - } - } - - @Override - public void downloadHasFinished() { - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - if (fragment instanceof DownloadIndexesThread.DownloadEvents && fragment.isAdded()) { - ((DownloadIndexesThread.DownloadEvents) fragment).downloadHasFinished(); - } - } - } - - @Override - public void onPluginStateChanged(OsmandPlugin plugin) { - getListAdapter().notifyDataSetChanged(); - } - - protected class PluginsListAdapter extends ArrayAdapter { - PluginsListAdapter() { - super(PluginsActivity.this, R.layout.plugins_list_item, new ArrayList<>()); - addAll(getMyApplication().getAidlApi().getConnectedApps()); - addAll(OsmandPlugin.getVisiblePlugins()); - } - - @NonNull - @Override - public View getView(int position, View convertView, @NonNull ViewGroup parent) { - View view = convertView; - if (view == null) { - view = getLayoutInflater().inflate(R.layout.plugins_list_item, parent, false); - } - - final Object item = getItem(position); - - boolean active = false; - int logoContDescId = R.string.shared_string_disable; - String name = ""; - boolean isLightTheme = getMyApplication().getSettings().isLightContent(); - - ImageButton pluginLogo = (ImageButton) view.findViewById(R.id.plugin_logo); - ImageView pluginOptions = (ImageView) view.findViewById(R.id.plugin_options); - TextView pluginDescription = (TextView) view.findViewById(R.id.plugin_description); - - if (item instanceof ConnectedApp) { - final ConnectedApp app = (ConnectedApp) item; - active = app.isEnabled(); - if (!active) { - logoContDescId = R.string.shared_string_enable; - } - name = app.getName(); - pluginDescription.setText(R.string.third_party_application); - pluginLogo.setImageDrawable(app.getIcon()); - pluginLogo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - switchEnabled(app); - } - }); - pluginOptions.setVisibility(View.GONE); - pluginOptions.setOnClickListener(null); - view.setTag(app); - } else if (item instanceof OsmandPlugin) { - final OsmandPlugin plugin = (OsmandPlugin) item; - active = plugin.isActive(); - if (!active) { - logoContDescId = plugin.needsInstallation() - ? R.string.access_shared_string_not_installed : R.string.shared_string_enable; - } - name = plugin.getName(); - pluginDescription.setText(plugin.getDescription()); - - boolean light = getMyApplication().getSettings().isLightContent(); - int linkTextColor = ContextCompat.getColor(PluginsActivity.this, - light ? R.color.ctx_menu_bottom_view_url_color_light : R.color.ctx_menu_bottom_view_url_color_dark); - - pluginDescription.setLinkTextColor(linkTextColor); - pluginDescription.setMovementMethod(LinkMovementMethod.getInstance()); - AndroidUtils.removeLinkUnderline(pluginDescription); - - OsmandApplication app = getMyApplication(); - int color = AndroidUtils.getColorFromAttr(PluginsActivity.this, R.attr.list_background_color); - pluginLogo.setImageDrawable(UiUtilities.tintDrawable(plugin.getLogoResource(), color)); - pluginLogo.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (plugin.isActive() || !plugin.needsInstallation()) { - enableDisablePlugin(plugin, !plugin.isActive()); - } - } - }); - pluginOptions.setVisibility(View.VISIBLE); - pluginOptions.setImageDrawable(getMyApplication().getUIUtilities().getThemedIcon(R.drawable.ic_overflow_menu_white)); - pluginOptions.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - showOptionsMenu(v, plugin); - } - }); - view.setTag(plugin); - } - - pluginLogo.setContentDescription(getString(logoContDescId)); - if (active) { - pluginLogo.setBackgroundResource(isLightTheme ? R.drawable.bg_plugin_logo_enabled_light : R.drawable.bg_plugin_logo_enabled_dark); - } else { - TypedArray attributes = getTheme().obtainStyledAttributes(new int[] {R.attr.bg_plugin_logo_disabled}); - pluginLogo.setBackgroundDrawable(attributes.getDrawable(0)); - attributes.recycle(); - } - - TextView pluginName = (TextView) view.findViewById(R.id.plugin_name); - pluginName.setText(name); - pluginName.setContentDescription(name + " " + getString(active - ? R.string.item_checked - : R.string.item_unchecked)); - - return view; - } - } - - private void showOptionsMenu(View v, final OsmandPlugin plugin) { - final Class settingsActivity = plugin.getSettingsActivity(); - - final PopupMenu optionsMenu = new PopupMenu(this, v); - if (plugin.isActive() || !plugin.needsInstallation()) { - MenuItem enableDisableItem = optionsMenu.getMenu().add( - plugin.isActive() ? R.string.shared_string_disable - : R.string.shared_string_enable); - enableDisableItem - .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - enableDisablePlugin(plugin, !plugin.isActive()); - optionsMenu.dismiss(); - return true; - } - }); - } - - if (settingsActivity != null && plugin.isActive()) { - MenuItem settingsItem = optionsMenu.getMenu().add(R.string.shared_string_settings); - settingsItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - startActivity(new Intent(PluginsActivity.this, settingsActivity)); - optionsMenu.dismiss(); - return true; - } - }); - } - - if (plugin instanceof CustomOsmandPlugin) { - MenuItem settingsItem = optionsMenu.getMenu().add(R.string.shared_string_delete); - settingsItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - showDeletePluginDialog((CustomOsmandPlugin) plugin); - optionsMenu.dismiss(); - return true; - } - }); - } - - optionsMenu.show(); - } - - private void showDeletePluginDialog(final CustomOsmandPlugin plugin) { - AlertDialog.Builder builder = new AlertDialog.Builder(PluginsActivity.this); - builder.setTitle(getString(R.string.delete_confirmation_msg, plugin.getName())); - builder.setMessage(R.string.are_you_sure); - builder.setNegativeButton(R.string.shared_string_cancel, null); - builder.setPositiveButton(R.string.shared_string_ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - OsmandApplication app = getMyApplication(); - OsmandPlugin.removeCustomPlugin(app, plugin); - getListAdapter().remove(plugin); - } - }); - builder.show(); - } -} diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java b/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java new file mode 100644 index 0000000000..783e7791c5 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java @@ -0,0 +1,338 @@ +package net.osmand.plus.activities; + +import android.app.Activity; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.TypedArray; +import android.os.Bundle; +import android.text.method.LinkMovementMethod; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.ListView; +import android.widget.TextView; + +import androidx.activity.OnBackPressedCallback; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.PopupMenu; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; + +import net.osmand.AndroidUtils; +import net.osmand.PlatformUtil; +import net.osmand.aidl.ConnectedApp; +import net.osmand.plus.CustomOsmandPlugin; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.base.BaseOsmAndFragment; +import net.osmand.plus.dialogs.PluginInstalledBottomSheetDialog.PluginStateListener; +import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; + +import org.apache.commons.logging.Log; + +import java.util.ArrayList; + +public class PluginsFragment extends BaseOsmAndFragment implements PluginStateListener { + + private static final Log log = PlatformUtil.getLog(PluginsFragment.class); + + public static final String TAG = PluginsFragment.class.getName(); + + public static final String OPEN_PLUGINS = "open_plugins"; + + private OsmandApplication app; + private PluginsListAdapter adapter; + + private LayoutInflater themedInflater; + private boolean nightMode; + + @Override + public int getStatusBarColorId() { + return nightMode ? R.color.status_bar_color_dark : R.color.status_bar_color_light; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + FragmentActivity activity = requireMyActivity(); + activity.getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { + public void handleOnBackPressed() { + FragmentActivity activity = getActivity(); + if (activity instanceof MapActivity) { + dismissImmediate(); + MapActivity mapActivity = (MapActivity) activity; + mapActivity.launchPrevActivityIntent(); + } + } + }); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + app = requireMyApplication(); + nightMode = !app.getSettings().isLightContent(); + + themedInflater = UiUtilities.getInflater(getContext(), nightMode); + View view = themedInflater.inflate(R.layout.plugins, container, false); + AndroidUtils.addStatusBarPadding21v(getContext(), view); + + TextView toolbarTitle = view.findViewById(R.id.toolbar_title); + toolbarTitle.setText(R.string.plugins_screen); + + ImageView closeButton = view.findViewById(R.id.close_button); + closeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Activity activity = getMyActivity(); + if (activity != null) { + activity.onBackPressed(); + } + } + }); + UiUtilities.rotateImageByLayoutDirection(closeButton, AndroidUtils.getLayoutDirection(app)); + + adapter = new PluginsListAdapter(requireContext()); + + ListView listView = view.findViewById(R.id.plugins_list); + listView.setAdapter(adapter); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + Object tag = view.getTag(); + if (tag instanceof OsmandPlugin) { + FragmentActivity activity = getActivity(); + if (activity != null) { + PluginInfoFragment.showInstance(activity.getSupportFragmentManager(), (OsmandPlugin) tag); + } + } else if (tag instanceof ConnectedApp) { + switchEnabled((ConnectedApp) tag); + } + } + }); + return view; + } + + @Override + public void onResume() { + super.onResume(); + OsmandPlugin.checkInstalledMarketPlugins(app, getActivity()); + adapter.notifyDataSetChanged(); + } + + private void enableDisablePlugin(OsmandPlugin plugin, boolean enable) { + if (OsmandPlugin.enablePlugin(getActivity(), app, plugin, enable)) { + adapter.notifyDataSetChanged(); + } + } + + private void switchEnabled(@NonNull ConnectedApp connectedApp) { + app.getAidlApi().switchEnabled(connectedApp); + adapter.notifyDataSetChanged(); + } + + @Override + public void onPluginStateChanged(OsmandPlugin plugin) { + adapter.notifyDataSetChanged(); + } + + protected class PluginsListAdapter extends ArrayAdapter { + + PluginsListAdapter(Context context) { + super(context, R.layout.plugins_list_item, new ArrayList<>()); + addAll(app.getAidlApi().getConnectedApps()); + addAll(OsmandPlugin.getVisiblePlugins()); + } + + @NonNull + @Override + public View getView(int position, View convertView, @NonNull ViewGroup parent) { + View view = convertView; + if (view == null) { + view = themedInflater.inflate(R.layout.plugins_list_item, parent, false); + } + Context context = view.getContext(); + + boolean active = false; + int logoContDescId = R.string.shared_string_disable; + String name = ""; + + ImageButton pluginLogo = view.findViewById(R.id.plugin_logo); + ImageView pluginOptions = view.findViewById(R.id.plugin_options); + TextView pluginDescription = view.findViewById(R.id.plugin_description); + + Object item = getItem(position); + if (item instanceof ConnectedApp) { + final ConnectedApp app = (ConnectedApp) item; + active = app.isEnabled(); + if (!active) { + logoContDescId = R.string.shared_string_enable; + } + name = app.getName(); + pluginDescription.setText(R.string.third_party_application); + pluginLogo.setImageDrawable(app.getIcon()); + pluginLogo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + switchEnabled(app); + } + }); + pluginOptions.setVisibility(View.GONE); + pluginOptions.setOnClickListener(null); + view.setTag(app); + } else if (item instanceof OsmandPlugin) { + final OsmandPlugin plugin = (OsmandPlugin) item; + active = plugin.isActive(); + if (!active) { + logoContDescId = plugin.needsInstallation() + ? R.string.access_shared_string_not_installed : R.string.shared_string_enable; + } + name = plugin.getName(); + pluginDescription.setText(plugin.getDescription()); + + int linkTextColorId = nightMode ? R.color.ctx_menu_bottom_view_url_color_dark : R.color.ctx_menu_bottom_view_url_color_light; + int linkTextColor = ContextCompat.getColor(context, linkTextColorId); + + pluginDescription.setLinkTextColor(linkTextColor); + pluginDescription.setMovementMethod(LinkMovementMethod.getInstance()); + AndroidUtils.removeLinkUnderline(pluginDescription); + + int color = AndroidUtils.getColorFromAttr(context, R.attr.list_background_color); + pluginLogo.setImageDrawable(UiUtilities.tintDrawable(plugin.getLogoResource(), color)); + pluginLogo.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (plugin.isActive() || !plugin.needsInstallation()) { + enableDisablePlugin(plugin, !plugin.isActive()); + } + } + }); + pluginOptions.setVisibility(View.VISIBLE); + pluginOptions.setImageDrawable(app.getUIUtilities().getThemedIcon(R.drawable.ic_overflow_menu_white)); + pluginOptions.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showOptionsMenu(v, plugin); + } + }); + view.setTag(plugin); + } + + pluginLogo.setContentDescription(getString(logoContDescId)); + if (active) { + pluginLogo.setBackgroundResource(nightMode ? R.drawable.bg_plugin_logo_enabled_dark : R.drawable.bg_plugin_logo_enabled_light); + } else { + TypedArray attributes = context.getTheme().obtainStyledAttributes(new int[] {R.attr.bg_plugin_logo_disabled}); + pluginLogo.setBackgroundDrawable(attributes.getDrawable(0)); + attributes.recycle(); + } + + TextView pluginName = view.findViewById(R.id.plugin_name); + pluginName.setText(name); + pluginName.setContentDescription(name + " " + getString(active + ? R.string.item_checked + : R.string.item_unchecked)); + + return view; + } + } + + private void showOptionsMenu(View view, final OsmandPlugin plugin) { + final PopupMenu optionsMenu = new PopupMenu(view.getContext(), view); + if (plugin.isActive() || !plugin.needsInstallation()) { + MenuItem enableDisableItem = optionsMenu.getMenu().add( + plugin.isActive() ? R.string.shared_string_disable + : R.string.shared_string_enable); + enableDisableItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + enableDisablePlugin(plugin, !plugin.isActive()); + optionsMenu.dismiss(); + return true; + } + }); + } + + final SettingsScreenType settingsScreenType = plugin.getSettingsScreenType(); + if (settingsScreenType != null && plugin.isActive()) { + MenuItem settingsItem = optionsMenu.getMenu().add(R.string.shared_string_settings); + settingsItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + FragmentActivity activity = getActivity(); + if (activity != null) { + BaseSettingsFragment.showInstance(activity, settingsScreenType); + } + optionsMenu.dismiss(); + return true; + } + }); + } + + if (plugin instanceof CustomOsmandPlugin) { + MenuItem settingsItem = optionsMenu.getMenu().add(R.string.shared_string_delete); + settingsItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + showDeletePluginDialog((CustomOsmandPlugin) plugin); + optionsMenu.dismiss(); + return true; + } + }); + } + + optionsMenu.show(); + } + + private void showDeletePluginDialog(final CustomOsmandPlugin plugin) { + Context context = getContext(); + if (context != null) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(getString(R.string.delete_confirmation_msg, plugin.getName())); + builder.setMessage(R.string.are_you_sure); + builder.setNegativeButton(R.string.shared_string_cancel, null); + builder.setPositiveButton(R.string.shared_string_ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + OsmandPlugin.removeCustomPlugin(app, plugin); + adapter.remove(plugin); + } + }); + builder.show(); + } + } + + public void dismissImmediate() { + FragmentActivity activity = getActivity(); + if (activity != null) { + try { + activity.getSupportFragmentManager().popBackStackImmediate(TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE); + } catch (Exception e) { + log.error(e); + } + } + } + + public static boolean showInstance(FragmentManager fragmentManager) { + try { + PluginsFragment fragment = new PluginsFragment(); + fragmentManager.beginTransaction() + .add(R.id.fragmentContainer, fragment, TAG) + .addToBackStack(TAG) + .commitAllowingStateLoss(); + return true; + } catch (Exception e) { + return false; + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java b/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java index 17559f0e9d..3509f99d65 100644 --- a/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java +++ b/OsmAnd/src/net/osmand/plus/activities/SettingsActivity.java @@ -1,11 +1,9 @@ package net.osmand.plus.activities; -import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceCategory; import android.preference.PreferenceScreen; @@ -59,33 +57,24 @@ public class SettingsActivity extends SettingsBaseActivity { } PreferenceCategory plugins = (PreferenceCategory) screen.findPreference("plugin_settings"); for(OsmandPlugin op : OsmandPlugin.getEnabledPlugins()) { - final Class sa = op.getSettingsActivity(); - if(sa != null) { - Preference preference = new Preference(this); - preference.setTitle(op.getName()); - preference.setKey(op.getId()); - preference.setOnPreferenceClickListener(new OnPreferenceClickListener() { - - @Override - public boolean onPreferenceClick(Preference preference) { - startActivity(new Intent(SettingsActivity.this, sa)); - return false; - } - }); - plugins.addPreference(preference); - } +// final Class sa = op.getSettingsActivity(); +// if(sa != null) { +// Preference preference = new Preference(this); +// preference.setTitle(op.getName()); +// preference.setKey(op.getId()); +// preference.setOnPreferenceClickListener(new OnPreferenceClickListener() { +// +// @Override +// public boolean onPreferenceClick(Preference preference) { +// startActivity(new Intent(SettingsActivity.this, sa)); +// return false; +// } +// }); +// plugins.addPreference(preference); +// } } } - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if ((requestCode == PLUGINS_SELECTION_REQUEST) && (resultCode == PluginsActivity.ACTIVE_PLUGINS_LIST_MODIFIED)) { - finish(); - startActivity(getIntent()); - } - } - @Override public boolean onPreferenceClick(Preference preference) { if (preference == general) { diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteMenuController.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteMenuController.java index f2f31e3a26..a0594ffc66 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteMenuController.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNoteMenuController.java @@ -96,13 +96,11 @@ public class AudioVideoNoteMenuController extends MenuController { @Override public Drawable getRightIcon() { - if (mRecording.isPhoto()) { - return getIcon(R.drawable.ic_action_photo_dark, R.color.audio_video_icon_color); - } else if (mRecording.isAudio()) { - return getIcon(R.drawable.ic_action_micro_dark, R.color.audio_video_icon_color); - } else { - return getIcon(R.drawable.ic_action_video_dark, R.color.audio_video_icon_color); + int iconId = AudioVideoNotesPlugin.getIconIdForRecordingFile(mRecording.getFile()); + if (iconId == -1) { + iconId = R.drawable.ic_action_photo_dark; } + return getIcon(iconId, R.color.audio_video_icon_color); } @NonNull diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index 0a99d15b03..c28b110c85 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -47,14 +47,11 @@ import net.osmand.PlatformUtil; import net.osmand.data.DataTileManager; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuAdapter.ItemClickListener; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.CommonPreference; -import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -66,12 +63,15 @@ import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; import net.osmand.plus.myplaces.FavoritesActivity; import net.osmand.plus.quickaction.QuickActionType; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; -import net.osmand.plus.views.layers.MapInfoLayer; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; -import net.osmand.plus.views.mapwidgets.widgetstates.WidgetState; +import net.osmand.plus.views.layers.MapInfoLayer; import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget; +import net.osmand.plus.views.mapwidgets.widgetstates.WidgetState; import net.osmand.util.Algorithms; import net.osmand.util.GeoPointParserUtil.GeoParsedPoint; import net.osmand.util.MapUtils; @@ -517,7 +517,18 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { } return additional.toString(); } + } + public static int getIconIdForRecordingFile(@NonNull File file) { + String fileName = file.getName(); + if (fileName.endsWith(IMG_EXTENSION)) { + return R.drawable.ic_action_photo_dark; + } else if (fileName.endsWith(MPEG4_EXTENSION)) { + return R.drawable.ic_action_video_dark; + } else if (fileName.endsWith(THREEGP_EXTENSION)) { + return R.drawable.ic_action_micro_dark; + } + return -1; } // private static void initializeRemoteControlRegistrationMethods() { @@ -1800,13 +1811,8 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { } @Override - public Class getSettingsActivity() { - return SettingsAudioVideoActivity.class; - } - - @Override - public Class getSettingsFragment() { - return MultimediaNotesFragment.class; + public SettingsScreenType getSettingsScreenType() { + return SettingsScreenType.MULTIMEDIA_NOTES; } @Override diff --git a/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java b/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java index 6996f7ee5b..4c1178ef61 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java @@ -13,6 +13,8 @@ import android.os.Build; import android.os.Bundle; import android.os.StatFs; import android.text.SpannableString; +import android.view.LayoutInflater; +import android.view.View; import androidx.annotation.NonNull; import androidx.core.app.ActivityCompat; @@ -22,17 +24,18 @@ import androidx.preference.PreferenceViewHolder; import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; -import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; import net.osmand.plus.profiles.SelectCopyAppModeBottomSheet; import net.osmand.plus.profiles.SelectCopyAppModeBottomSheet.CopyAppModePrefsListener; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.settings.bottomsheets.ResetProfilePrefsBottomSheet; import net.osmand.plus.settings.bottomsheets.ResetProfilePrefsBottomSheet.ResetAppModePrefsListener; +import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.settings.preferences.ListPreferenceEx; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; import net.osmand.plus.widgets.style.CustomTypefaceSpan; @@ -43,6 +46,7 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import static net.osmand.plus.activities.PluginInfoFragment.PLUGIN_INFO; import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AUDIO_BITRATE_DEFAULT; import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_CAMERA_FOCUS_AUTO; import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_CAMERA_FOCUS_CONTINUOUS; @@ -66,6 +70,35 @@ public class MultimediaNotesFragment extends BaseSettingsFragment implements Cop private static final String RESET_TO_DEFAULT = "reset_to_default"; private static final String OPEN_NOTES = "open_notes"; + boolean showSwitchProfile = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + if (args != null) { + showSwitchProfile = args.getBoolean(PLUGIN_INFO, false); + } + } + + @Override + protected void createToolbar(LayoutInflater inflater, View view) { + super.createToolbar(inflater, view); + + View switchProfile = view.findViewById(R.id.profile_button); + if (switchProfile != null) { + AndroidUiHelper.updateVisibility(switchProfile, showSwitchProfile); + } + } + + @Override + public Bundle buildArguments() { + Bundle args = super.buildArguments(); + args.putBoolean(PLUGIN_INFO, showSwitchProfile); + return args; + } + @Override protected void setupPreferences() { AudioVideoNotesPlugin plugin = OsmandPlugin.getPlugin(AudioVideoNotesPlugin.class); diff --git a/OsmAnd/src/net/osmand/plus/audionotes/SettingsAudioVideoActivity.java b/OsmAnd/src/net/osmand/plus/audionotes/SettingsAudioVideoActivity.java deleted file mode 100644 index 815b77108c..0000000000 --- a/OsmAnd/src/net/osmand/plus/audionotes/SettingsAudioVideoActivity.java +++ /dev/null @@ -1,334 +0,0 @@ -package net.osmand.plus.audionotes; - -import android.hardware.Camera; -import android.hardware.Camera.Parameters; -import android.media.CamcorderProfile; -import android.media.MediaRecorder; -import android.os.Build; -import android.os.Bundle; -import android.os.StatFs; -import android.preference.ListPreference; -import android.preference.PreferenceCategory; -import android.preference.PreferenceScreen; - -import net.osmand.AndroidUtils; -import net.osmand.PlatformUtil; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.R; -import net.osmand.plus.activities.SettingsBaseActivity; - -import org.apache.commons.logging.Log; - -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AUDIO_BITRATE_DEFAULT; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_CAMERA_FOCUS_AUTO; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_CAMERA_FOCUS_CONTINUOUS; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_CAMERA_FOCUS_EDOF; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_CAMERA_FOCUS_HIPERFOCAL; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_CAMERA_FOCUS_INFINITY; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_CAMERA_FOCUS_MACRO; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_DEFAULT_ACTION_AUDIO; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_DEFAULT_ACTION_CHOOSE; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_DEFAULT_ACTION_TAKEPICTURE; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.AV_DEFAULT_ACTION_VIDEO; -import static net.osmand.plus.audionotes.AudioVideoNotesPlugin.cameraPictureSizeDefault; - -// camera picture size: -// support camera focus select: -//// - -public class SettingsAudioVideoActivity extends SettingsBaseActivity { - - private static final Log log = PlatformUtil.getLog(AudioVideoNotesPlugin.class); - - - @Override - public void onCreate(Bundle savedInstanceState) { - ((OsmandApplication) getApplication()).applyTheme(this); - super.onCreate(savedInstanceState); - getToolbar().setTitle(R.string.av_settings); - PreferenceScreen grp = getPreferenceScreen(); - AudioVideoNotesPlugin p = OsmandPlugin.getEnabledPlugin(AudioVideoNotesPlugin.class); - if (p != null) { - String[] entries; - Integer[] intValues; - - entries = new String[]{getString(R.string.av_def_action_choose), getString(R.string.av_def_action_audio), - getString(R.string.av_def_action_video), getString(R.string.av_def_action_picture)}; - intValues = new Integer[]{AV_DEFAULT_ACTION_CHOOSE, AV_DEFAULT_ACTION_AUDIO, AV_DEFAULT_ACTION_VIDEO, - AV_DEFAULT_ACTION_TAKEPICTURE}; - ListPreference defAct = createListPreference(p.AV_DEFAULT_ACTION, entries, intValues, R.string.av_widget_action, - R.string.av_widget_action_descr); - grp.addPreference(defAct); - - PreferenceCategory photo = new PreferenceCategory(this); - photo.setTitle(R.string.shared_string_photo); - grp.addPreference(photo); - - final Camera cam = openCamera(); - if (cam != null) { - // camera type settings - photo.addPreference(createCheckBoxPreference(p.AV_EXTERNAL_PHOTO_CAM, R.string.av_use_external_camera, - R.string.av_use_external_camera_descr)); - - Parameters parameters = cam.getParameters(); - createCameraPictureSizesPref(p, photo, parameters); - createCameraFocusModesPref(p, photo, parameters); - - // play sound on success photo - photo.addPreference(createCheckBoxPreference(p.AV_PHOTO_PLAY_SOUND, R.string.av_photo_play_sound, - R.string.av_photo_play_sound_descr)); - - cam.release(); - } - - // video settings - PreferenceCategory video = new PreferenceCategory(this); - video.setTitle(R.string.shared_string_video); - grp.addPreference(video); - - video.addPreference(createCheckBoxPreference(p.AV_EXTERNAL_RECORDER, R.string.av_use_external_recorder, - R.string.av_use_external_recorder_descr)); - -// entries = new String[] { "3GP", "MP4" }; -// intValues = new Integer[] { VIDEO_OUTPUT_3GP, VIDEO_OUTPUT_MP4 }; -// ListPreference lp = createListPreference(p.AV_VIDEO_FORMAT, entries, intValues, R.string.av_video_format, -// R.string.av_video_format_descr); -// video.addPreference(lp); - - List qNames = new ArrayList<>(); - List qValues = new ArrayList<>(); - if (Build.VERSION.SDK_INT < 11 || CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_LOW)) { - qNames.add(getString(R.string.av_video_quality_low)); - qValues.add(CamcorderProfile.QUALITY_LOW); - } - if (Build.VERSION.SDK_INT >= 11 && CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) { - qNames.add("720 x 480 (480p)"); - qValues.add(CamcorderProfile.QUALITY_480P); - } - if (Build.VERSION.SDK_INT >= 11 && CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) { - qNames.add("1280 x 720 (720p)"); - qValues.add(CamcorderProfile.QUALITY_720P); - } - if (Build.VERSION.SDK_INT >= 11 && CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) { - qNames.add("1920 x 1080 (1080p)"); - qValues.add(CamcorderProfile.QUALITY_1080P); - } - if (Build.VERSION.SDK_INT >= 21 && CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) { - qNames.add("3840x2160 (2160p)"); - qValues.add(CamcorderProfile.QUALITY_2160P); - } - if (Build.VERSION.SDK_INT < 11 || CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_HIGH)) { - qNames.add(getString(R.string.av_video_quality_high)); - qValues.add(CamcorderProfile.QUALITY_HIGH); - } - - ListPreference lp = createListPreference(p.AV_VIDEO_QUALITY, - qNames.toArray(new String[qNames.size()]), - qValues.toArray(new Integer[qValues.size()]), - R.string.av_video_quality, - R.string.av_video_quality_descr); - video.addPreference(lp); - - // Recorder Split settings - PreferenceCategory recSplit = new PreferenceCategory(this); - recSplit.setTitle(R.string.rec_split); - grp.addPreference(recSplit); - - recSplit.addPreference(createCheckBoxPreference(p.AV_RECORDER_SPLIT, R.string.rec_split_title, - R.string.rec_split_desc)); - - intValues = new Integer[]{1, 2, 3, 4, 5, 7, 10, 15, 20, 25, 30}; - entries = new String[intValues.length]; - int i = 0; - String minStr = getString(R.string.int_min); - for (int v : intValues) { - entries[i++] = String.valueOf(v) + " " + minStr; - } - lp = createListPreference(p.AV_RS_CLIP_LENGTH, entries, intValues, - R.string.rec_split_clip_length, - R.string.rec_split_clip_length_desc); - recSplit.addPreference(lp); - - File dir = getMyApplication().getAppPath("").getParentFile(); - long size = 0; - if (dir.canRead()) { - StatFs fs = new StatFs(dir.getAbsolutePath()); - size = ((long) fs.getBlockSize() * (long) fs.getBlockCount()) / (1 << 30); - } - if (size > 0) { - int value = 1; - ArrayList gbList = new ArrayList<>(); - while (value < size) { - gbList.add(value); - if (value < 5) { - value++; - } else { - value += 5; - } - } - if (value != size) { - gbList.add((int) size); - } - entries = new String[gbList.size()]; - intValues = new Integer[gbList.size()]; - i = 0; - for (int v : gbList) { - intValues[i] = v; - entries[i] = AndroidUtils.formatSize(this, v * (1l << 30)); - i++; - } - - lp = createListPreference(p.AV_RS_STORAGE_SIZE, entries, intValues, - R.string.rec_split_storage_size, - R.string.rec_split_storage_size_desc); - recSplit.addPreference(lp); - } - - // audio settings - PreferenceCategory audio = new PreferenceCategory(this); - audio.setTitle(R.string.shared_string_audio); - grp.addPreference(audio); - - entries = new String[]{"Default", "AAC"}; - intValues = new Integer[]{MediaRecorder.AudioEncoder.DEFAULT, MediaRecorder.AudioEncoder.AAC}; - lp = createListPreference(p.AV_AUDIO_FORMAT, entries, intValues, - R.string.av_audio_format, - R.string.av_audio_format_descr); - audio.addPreference(lp); - - entries = new String[]{"Default", "16 kbps", "32 kbps", "48 kbps", "64 kbps", "96 kbps", "128 kbps"}; - intValues = new Integer[]{AUDIO_BITRATE_DEFAULT, 16 * 1024, 32 * 1024, 48 * 1024, 64 * 1024, 96 * 1024, 128 * 1024}; - lp = createListPreference(p.AV_AUDIO_BITRATE, entries, intValues, - R.string.av_audio_bitrate, - R.string.av_audio_bitrate_descr); - audio.addPreference(lp); - } - } - - private void createCameraPictureSizesPref(AudioVideoNotesPlugin p, PreferenceCategory photo, Parameters parameters) { - String[] entries; - Integer[] intValues; - // Photo picture size - // get supported sizes - List psps = parameters.getSupportedPictureSizes(); - if (psps == null) { - return; - } - // list of megapixels of each resolution - List mpix = new ArrayList(); - // list of index each resolution in list, returned by getSupportedPictureSizes() - List picSizesValues = new ArrayList(); - // fill lists for sort - for (int index = 0; index < psps.size(); index++) { - mpix.add((psps.get(index)).width * (psps.get(index)).height); - picSizesValues.add(index); - } - // sort list for max resolution in begining of list - for (int i = 0; i < mpix.size(); i++) { - for (int j = 0; j < mpix.size() - i - 1; j++) { - if (mpix.get(j) < mpix.get(j + 1)) { - // change elements - int tmp = mpix.get(j + 1); - mpix.set(j + 1, mpix.get(j)); - mpix.set(j, tmp); - - tmp = picSizesValues.get(j + 1); - picSizesValues.set(j + 1, picSizesValues.get(j)); - picSizesValues.set(j, tmp); - } - } - } - // set default photo size to max resolution (set index of element with max resolution in List, returned by getSupportedPictureSizes() ) - cameraPictureSizeDefault = picSizesValues.get(0); - log.debug("onCreate() set cameraPictureSizeDefault=" + cameraPictureSizeDefault); - - List itemsPicSizes = new ArrayList(); - String prefix; - for (int index = 0; index < psps.size(); index++) { - float px = (float) ((psps.get(picSizesValues.get(index))).width * (psps.get(picSizesValues.get(index))).height); - if (px > 102400) // 100 K - { - px = px / 1048576; - prefix = "Mpx"; - } else { - px = px / 1024; - prefix = "Kpx"; - } - - itemsPicSizes.add((psps.get(picSizesValues.get(index))).width + - "x" + - (psps.get(picSizesValues.get(index))).height + - " ( " + - String.format("%.2f", px) + - " " + - prefix + - " )"); - } - log.debug("onCreate() set default size: width=" + psps.get(cameraPictureSizeDefault).width + " height=" - + psps.get(cameraPictureSizeDefault).height + " index in ps=" + cameraPictureSizeDefault); - - entries = itemsPicSizes.toArray(new String[itemsPicSizes.size()]); - intValues = picSizesValues.toArray(new Integer[picSizesValues.size()]); - if (entries.length > 0) { - ListPreference camSizes = createListPreference(p.AV_CAMERA_PICTURE_SIZE, entries, intValues, R.string.av_camera_pic_size, - R.string.av_camera_pic_size_descr); - photo.addPreference(camSizes); - } - } - - private void createCameraFocusModesPref(AudioVideoNotesPlugin p, PreferenceCategory photo, Parameters parameters) { - String[] entries; - Integer[] intValues; - // focus mode settings - // show in menu only suppoted modes - List sfm = parameters.getSupportedFocusModes(); - if (sfm == null) { - return; - } - List items = new ArrayList(); - List itemsValues = new ArrayList(); - // filtering known types for translate and set index - for (int index = 0; index < sfm.size(); index++) { - if (sfm.get(index).equals("auto")) { - items.add(getString(R.string.av_camera_focus_auto)); - itemsValues.add(AV_CAMERA_FOCUS_AUTO); - } else if (sfm.get(index).equals("fixed")) { - items.add(getString(R.string.av_camera_focus_hiperfocal)); - itemsValues.add(AV_CAMERA_FOCUS_HIPERFOCAL); - } else if (sfm.get(index).equals("edof")) { - items.add(getString(R.string.av_camera_focus_edof)); - itemsValues.add(AV_CAMERA_FOCUS_EDOF); - } else if (sfm.get(index).equals("infinity")) { - items.add(getString(R.string.av_camera_focus_infinity)); - itemsValues.add(AV_CAMERA_FOCUS_INFINITY); - } else if (sfm.get(index).equals("macro")) { - items.add(getString(R.string.av_camera_focus_macro)); - itemsValues.add(AV_CAMERA_FOCUS_MACRO); - } else if (sfm.get(index).equals("continuous-picture")) { - items.add(getString(R.string.av_camera_focus_continuous)); - itemsValues.add(AV_CAMERA_FOCUS_CONTINUOUS); - } - } - entries = items.toArray(new String[items.size()]); - intValues = itemsValues.toArray(new Integer[itemsValues.size()]); - if (entries.length > 0) { - ListPreference camFocus = createListPreference(p.AV_CAMERA_FOCUS_TYPE, entries, intValues, R.string.av_camera_focus, - R.string.av_camera_focus_descr); - photo.addPreference(camFocus); - } - } - - protected Camera openCamera() { - try { - return Camera.open(); - } catch (Exception e) { - log.error("Error open camera", e); - return null; - } - } -} diff --git a/OsmAnd/src/net/osmand/plus/dashboard/DashPluginsFragment.java b/OsmAnd/src/net/osmand/plus/dashboard/DashPluginsFragment.java index 20affaa27a..15d222a980 100644 --- a/OsmAnd/src/net/osmand/plus/dashboard/DashPluginsFragment.java +++ b/OsmAnd/src/net/osmand/plus/dashboard/DashPluginsFragment.java @@ -14,11 +14,12 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; -import net.osmand.plus.activities.PluginActivity; +import net.osmand.plus.activities.PluginsFragment; import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; import net.osmand.plus.dashboard.tools.DashFragmentData; import net.osmand.plus.development.OsmandDevelopmentPlugin; @@ -68,9 +69,10 @@ public class DashPluginsFragment extends DashBaseFragment { return new View.OnClickListener() { @Override public void onClick(View view) { - Intent intent = new Intent(getActivity(), PluginActivity.class); - intent.putExtra(PluginActivity.EXTRA_PLUGIN_ID, plugin.getId()); - startActivity(intent); + FragmentActivity activity = getActivity(); + if (activity != null) { + PluginsFragment.showInstance(activity.getSupportFragmentManager()); + } closeDashboard(); } }; @@ -84,7 +86,10 @@ public class DashPluginsFragment extends DashBaseFragment { view.findViewById(R.id.show_all).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - startActivity(new Intent(getActivity(), getMyApplication().getAppCustomization().getPluginsActivity())); + FragmentActivity activity = getActivity(); + if (activity != null) { + PluginsFragment.showInstance(activity.getSupportFragmentManager()); + } closeDashboard(); } }); diff --git a/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java b/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java index 6786e78d70..d7798a646b 100644 --- a/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java +++ b/OsmAnd/src/net/osmand/plus/development/OsmandDevelopmentPlugin.java @@ -14,7 +14,7 @@ import net.osmand.plus.Version; import net.osmand.plus.activities.ContributionVersionActivity; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.dashboard.tools.DashFragmentData; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import net.osmand.plus.views.layers.MapInfoLayer; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; @@ -122,13 +122,8 @@ public class OsmandDevelopmentPlugin extends OsmandPlugin { } @Override - public Class getSettingsActivity() { - return SettingsDevelopmentActivity.class; - } - - @Override - public Class getSettingsFragment() { - return DevelopmentSettingsFragment.class; + public SettingsScreenType getSettingsScreenType() { + return SettingsScreenType.DEVELOPMENT_SETTINGS; } @Override diff --git a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java b/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java deleted file mode 100644 index c500a28254..0000000000 --- a/OsmAnd/src/net/osmand/plus/development/SettingsDevelopmentActivity.java +++ /dev/null @@ -1,212 +0,0 @@ -package net.osmand.plus.development; - - -import android.annotation.SuppressLint; -import android.content.Intent; -import android.os.Bundle; -import android.os.Debug; -import android.os.Debug.MemoryInfo; -import android.preference.CheckBoxPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceCategory; -import android.preference.PreferenceScreen; - -import net.osmand.plus.OsmAndLocationSimulation; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.R; -import net.osmand.plus.Version; -import net.osmand.plus.activities.SettingsBaseActivity; -import net.osmand.plus.render.NativeOsmandLibrary; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.util.SunriseSunset; - -import java.text.SimpleDateFormat; - -//import net.osmand.plus.development.OsmandDevelopmentPlugin; - -public class SettingsDevelopmentActivity extends SettingsBaseActivity { - - @SuppressLint("SimpleDateFormat") - @Override - public void onCreate(Bundle savedInstanceState) { - OsmandApplication app = getMyApplication(); - app.applyTheme(this); - super.onCreate(savedInstanceState); - getToolbar().setTitle(R.string.debugging_and_development); - PreferenceScreen category = getPreferenceScreen(); - Preference pref; - - if (Version.isOpenGlAvailable(app)) { - category.addPreference(createCheckBoxPreference(settings.USE_OPENGL_RENDER, - R.string.use_opengl_render, R.string.use_opengl_render_descr)); - } - - if (!Version.isBlackberry(app)) { - CheckBoxPreference nativeCheckbox = createCheckBoxPreference(settings.SAFE_MODE, R.string.safe_mode, R.string.safe_mode_description); - // disable the checkbox if the library cannot be used - if ((NativeOsmandLibrary.isLoaded() && !NativeOsmandLibrary.isSupported()) || settings.NATIVE_RENDERING_FAILED.get()) { - nativeCheckbox.setEnabled(false); - nativeCheckbox.setChecked(true); - } - category.addPreference(nativeCheckbox); - } - - PreferenceCategory navigation = new PreferenceCategory(this); - navigation.setTitle(R.string.routing_settings); - category.addPreference(navigation); - pref = new Preference(this); - final Preference simulate = pref; - final OsmAndLocationSimulation sim = getMyApplication().getLocationProvider().getLocationSimulation(); - final Runnable updateTitle = new Runnable(){ - - @Override - public void run() { - simulate.setSummary(sim.isRouteAnimating() ? - R.string.simulate_your_location_stop_descr : R.string.simulate_your_location_gpx_descr); - } - }; - pref.setTitle(R.string.simulate_your_location); - updateTitle.run(); - pref.setKey("simulate_your_location"); - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - updateTitle.run(); - sim.startStopRouteAnimation(SettingsDevelopmentActivity.this, true, updateTitle); - return true; - } - }); - navigation.addPreference(pref); - - PreferenceCategory debug = new PreferenceCategory(this); - debug.setTitle(R.string.debugging_and_development); - category.addPreference(debug); - - CheckBoxPreference dbg = createCheckBoxPreference(settings.DEBUG_RENDERING_INFO, - R.string.trace_rendering, R.string.trace_rendering_descr); - debug.addPreference(dbg); - - - final Preference firstRunPreference = new Preference(this); - firstRunPreference.setTitle(R.string.simulate_initial_startup); - firstRunPreference.setSummary(R.string.simulate_initial_startup_descr); - firstRunPreference.setSelectable(true); - firstRunPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - getMyApplication().getAppInitializer().resetFirstTimeRun(); - OsmandSettings settings = getMyApplication().getSettings(); - settings.FIRST_MAP_IS_DOWNLOADED.set(false); - settings.MAPILLARY_FIRST_DIALOG_SHOWN.set(false); - settings.WEBGL_SUPPORTED.set(true); - settings.WIKI_ARTICLE_SHOW_IMAGES_ASKED.set(false); - - getMyApplication().showToastMessage(R.string.shared_string_ok); - return true; - } - }); - debug.addPreference(firstRunPreference); - - debug.addPreference(createCheckBoxPreference(settings.SHOULD_SHOW_FREE_VERSION_BANNER, - R.string.show_free_version_banner, - R.string.show_free_version_banner_description)); - - pref = new Preference(this); - pref.setTitle(R.string.test_voice_prompts); - pref.setSummary(R.string.play_commands_of_currently_selected_voice); - pref.setKey("test_voice_commands"); - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - startActivity(new Intent(SettingsDevelopmentActivity.this, TestVoiceActivity.class)); - return true; - } - }); - category.addPreference(pref); - - pref = new Preference(this); - pref.setTitle(R.string.logcat_buffer); - pref.setSummary(R.string.logcat_buffer_descr); - pref.setKey("logcat_buffer"); - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - startActivity(new Intent(SettingsDevelopmentActivity.this, LogcatActivity.class)); - return true; - } - }); - category.addPreference(pref); - - PreferenceCategory info = new PreferenceCategory(this); - info.setTitle(R.string.info_button); - category.addPreference(info); - - pref = new Preference(this); - pref.setTitle(R.string.global_app_allocated_memory); - - long javaAvailMem = (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory())/ (1024*1024l); - long javaTotal = Runtime.getRuntime().totalMemory() / (1024*1024l); - long dalvikSize = android.os.Debug.getNativeHeapAllocatedSize() / (1024*1024l); - pref.setSummary(getString(R.string.global_app_allocated_memory_descr, javaAvailMem, javaTotal, dalvikSize)); - pref.setSelectable(false); - //setEnabled(false) creates bad readability on some devices - //pref.setEnabled(false); - info.addPreference(pref); - -// ActivityManager activityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE); -// ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); -// activityManager.getMemoryInfo(memoryInfo); -// long totalSize = memoryInfo.availMem / (1024*1024l); - MemoryInfo mem = new Debug.MemoryInfo(); - Debug.getMemoryInfo(mem); - pref = new Preference(this); - pref.setTitle(R.string.native_app_allocated_memory); - pref.setSummary(getString(R.string.native_app_allocated_memory_descr - , mem.nativePrivateDirty / 1024, mem.dalvikPrivateDirty / 1024 , mem.otherPrivateDirty / 1024 - , mem.nativePss / 1024, mem.dalvikPss / 1024 , mem.otherPss / 1024)); - pref.setSelectable(false); - //setEnabled(false) creates bad readability on some devices - //pref.setEnabled(false); - info.addPreference(pref); - - final Preference agpspref = new Preference(this); - agpspref.setTitle(R.string.agps_info); - if (settings.AGPS_DATA_LAST_TIME_DOWNLOADED.get() != 0L) { - SimpleDateFormat prt = new SimpleDateFormat("yyyy-MM-dd HH:mm"); - agpspref.setSummary(getString(R.string.agps_data_last_downloaded, prt.format(settings.AGPS_DATA_LAST_TIME_DOWNLOADED.get()))); - } else { - agpspref.setSummary(getString(R.string.agps_data_last_downloaded, "--")); - } - agpspref.setSelectable(true); - //setEnabled(false) creates bad readability on some devices - //pref.setEnabled(false); - agpspref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - if(getMyApplication().getSettings().isInternetConnectionAvailable(true)) { - getMyApplication().getLocationProvider().redownloadAGPS(); - SimpleDateFormat prt = new SimpleDateFormat("yyyy-MM-dd HH:mm"); - agpspref.setSummary(getString(R.string.agps_data_last_downloaded, prt.format(settings.AGPS_DATA_LAST_TIME_DOWNLOADED.get()))); - } - return true; - } - }); - info.addPreference(agpspref); - - SunriseSunset sunriseSunset = getMyApplication().getDaynightHelper().getSunriseSunset(); - pref = new Preference(this); - pref.setTitle(R.string.day_night_info); - if (sunriseSunset != null && sunriseSunset.getSunrise() != null && sunriseSunset.getSunset() != null) { - SimpleDateFormat prt = new SimpleDateFormat("yyyy-MM-dd HH:mm"); - pref.setSummary(getString(R.string.day_night_info_description, prt.format(sunriseSunset.getSunrise()), - prt.format(sunriseSunset.getSunset()))); - } else { - pref.setSummary(getString(R.string.day_night_info_description, "null", "null")); - } - pref.setSelectable(false); - //setEnabled(false) creates bad readability on some devices - //pref.setEnabled(false); - info.addPreference(pref); - } -} diff --git a/OsmAnd/src/net/osmand/plus/dialogs/MapLayerMenuListener.java b/OsmAnd/src/net/osmand/plus/dialogs/MapLayerMenuListener.java index 9234239040..29a64bcee4 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/MapLayerMenuListener.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/MapLayerMenuListener.java @@ -1,7 +1,6 @@ package net.osmand.plus.dialogs; import android.content.DialogInterface; -import android.content.Intent; import android.view.View; import android.widget.ArrayAdapter; import android.widget.CompoundButton; @@ -20,7 +19,7 @@ import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivityLayers; -import net.osmand.plus.activities.PluginActivity; +import net.osmand.plus.activities.PluginsFragment; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.poi.PoiFiltersHelper; import net.osmand.plus.poi.PoiUIFilter; @@ -149,9 +148,7 @@ final class MapLayerMenuListener extends OnRowItemClick { settings.SHOW_MAP_MARKERS.set(isChecked); } else if (itemId == R.string.layer_map) { if (OsmandPlugin.getEnabledPlugin(OsmandRasterMapsPlugin.class) == null) { - Intent intent = new Intent(mapActivity, PluginActivity.class); - intent.putExtra(PluginActivity.EXTRA_PLUGIN_ID, OsmandRasterMapsPlugin.ID); - mapActivity.startActivity(intent); + PluginsFragment.showInstance(mapActivity.getSupportFragmentManager()); } else { ContextMenuItem it = adapter.getItem(pos); mapActivity.getMapLayers().selectMapLayer(mapActivity.getMapView(), it, adapter); diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index ec1c70fd41..e4cdc5ce94 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.AsyncTask; +import android.os.Bundle; import android.util.TypedValue; import android.view.MenuItem; import android.view.View; @@ -25,6 +26,8 @@ import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.LocalIndexHelper.LocalIndexType; import net.osmand.plus.activities.LocalIndexInfo; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.PluginsFragment; import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; import net.osmand.plus.download.CityItem; import net.osmand.plus.download.CustomIndexItem; @@ -343,8 +346,7 @@ public class ItemViewHolder { ChoosePlanDialogFragment.showSeaDepthMapsInstance(context.getSupportFragmentManager()); break; case ASK_FOR_SEAMARKS_PLUGIN: - context.startActivity(new Intent(context, context.getMyApplication().getAppCustomization() - .getPluginsActivity())); + showPluginsScreen(); Toast.makeText(context.getApplicationContext(), context.getString(R.string.activate_seamarks_plugin), Toast.LENGTH_SHORT).show(); break; @@ -352,8 +354,7 @@ public class ItemViewHolder { ChoosePlanDialogFragment.showHillshadeSrtmPluginInstance(context.getSupportFragmentManager()); break; case ASK_FOR_SRTM_PLUGIN_ENABLE: - context.startActivity(new Intent(context, context.getMyApplication().getAppCustomization() - .getPluginsActivity())); + showPluginsScreen(); Toast.makeText(context, context.getString(R.string.activate_srtm_plugin), Toast.LENGTH_SHORT).show(); break; @@ -361,6 +362,13 @@ public class ItemViewHolder { break; } } + + private void showPluginsScreen() { + Bundle params = new Bundle(); + params.putBoolean(PluginsFragment.OPEN_PLUGINS, true); + Intent intent = context.getIntent(); + MapActivity.launchMapActivityMoveToTop(context, intent != null ? intent.getExtras() : null, null, params); + } }; } else { final boolean isDownloading = context.getDownloadThread().isDownloading(item); diff --git a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java index 47650dc2ec..dd7e804b4c 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java @@ -13,6 +13,7 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Build; +import android.os.Bundle; import android.text.SpannableString; import android.text.style.StyleSpan; import android.view.ContextThemeWrapper; @@ -80,6 +81,7 @@ import net.osmand.plus.OsmAndConstants; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.activities.PluginsFragment; import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.helpers.enums.SpeedConstants; import net.osmand.plus.settings.backend.CommonPreference; @@ -90,7 +92,6 @@ import net.osmand.plus.Version; import net.osmand.plus.activities.ActivityResultListener; import net.osmand.plus.activities.ActivityResultListener.OnActivityResultListener; import net.osmand.plus.activities.MapActivity; -import net.osmand.plus.activities.PluginActivity; import net.osmand.plus.activities.SettingsActivity; import net.osmand.plus.dialogs.ConfigureMapMenu; import net.osmand.plus.dialogs.GpxAppearanceAdapter; @@ -649,9 +650,9 @@ public class GpxUiHelper { confirm.setPositiveButton(R.string.shared_string_ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - Intent intent = new Intent(activity, PluginActivity.class); - intent.putExtra(PluginActivity.EXTRA_PLUGIN_ID, OsmandMonitoringPlugin.ID); - activity.startActivity(intent); + Bundle params = new Bundle(); + params.putBoolean(PluginsFragment.OPEN_PLUGINS, true); + MapActivity.launchMapActivityMoveToTop(activity, null, null, params); } }); confirm.setNegativeButton(R.string.shared_string_cancel, null); diff --git a/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java b/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java index d58271a27c..19748b100c 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java @@ -11,16 +11,18 @@ import net.osmand.PlatformUtil; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.map.TileSourceManager; -import net.osmand.plus.mapsource.EditMapSourceDialogFragment; -import net.osmand.plus.search.QuickSearchDialogFragment; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.MapMarkersHelper; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.PluginsFragment; import net.osmand.plus.dashboard.DashboardOnMap.DashboardType; import net.osmand.plus.mapmarkers.MapMarkersDialogFragment; +import net.osmand.plus.mapsource.EditMapSourceDialogFragment; +import net.osmand.plus.osmedit.OsmEditingFragment; +import net.osmand.plus.search.QuickSearchDialogFragment; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import net.osmand.util.Algorithms; @@ -58,6 +60,9 @@ public class IntentHelper { if (!applied) { applied = parseSendIntent(); } + if (!applied) { + applied = parseOAuthIntent(); + } return applied; } @@ -207,10 +212,22 @@ public class IntentHelper { mapActivity.setIntent(null); } if (intent.hasExtra(BaseSettingsFragment.OPEN_SETTINGS)) { - String settingsType = intent.getStringExtra(BaseSettingsFragment.OPEN_SETTINGS); String appMode = intent.getStringExtra(BaseSettingsFragment.APP_MODE_KEY); - if (BaseSettingsFragment.OPEN_CONFIG_PROFILE.equals(settingsType)) { - BaseSettingsFragment.showInstance(mapActivity, SettingsScreenType.CONFIGURE_PROFILE, ApplicationMode.valueOfStringKey(appMode, null)); + String settingsTypeName = intent.getStringExtra(BaseSettingsFragment.OPEN_SETTINGS); + if (!Algorithms.isEmpty(settingsTypeName)) { + try { + SettingsScreenType screenType = SettingsScreenType.valueOf(settingsTypeName); + BaseSettingsFragment.showInstance(mapActivity, screenType, ApplicationMode.valueOfStringKey(appMode, null)); + } catch (IllegalArgumentException e) { + LOG.error("error", e); + } + } + mapActivity.setIntent(null); + } + if (intent.hasExtra(PluginsFragment.OPEN_PLUGINS)) { + boolean openPlugins = intent.getBooleanExtra(PluginsFragment.OPEN_PLUGINS, false); + if (openPlugins) { + PluginsFragment.showInstance(mapActivity.getSupportFragmentManager()); } mapActivity.setIntent(null); } @@ -271,6 +288,23 @@ public class IntentHelper { return false; } + private boolean parseOAuthIntent() { + Intent intent = mapActivity.getIntent(); + if (intent != null && intent.getData() != null) { + Uri uri = intent.getData(); + if (uri.toString().startsWith("osmand-oauth")) { + OsmEditingFragment fragment = mapActivity.getOsmEditingFragment(); + if (fragment != null) { + String oauthVerifier = uri.getQueryParameter("oauth_verifier"); + fragment.authorize(oauthVerifier); + mapActivity.setIntent(null); + return true; + } + } + } + return false; + } + private boolean handleSendText(Intent intent) { String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); if (!Algorithms.isEmpty(sharedText)) { diff --git a/OsmAnd/src/net/osmand/plus/importfiles/ImportHelper.java b/OsmAnd/src/net/osmand/plus/importfiles/ImportHelper.java index 1059a46bb0..1416e29e31 100644 --- a/OsmAnd/src/net/osmand/plus/importfiles/ImportHelper.java +++ b/OsmAnd/src/net/osmand/plus/importfiles/ImportHelper.java @@ -34,8 +34,8 @@ import net.osmand.plus.dialogs.ImportGpxBottomSheetDialogFragment; import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper.GPXInfo; import net.osmand.plus.measurementtool.MeasurementToolFragment; -import net.osmand.plus.settings.backend.SettingsHelper; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsHelper; +import net.osmand.plus.settings.backend.backup.SettingsItem; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.util.Algorithms; diff --git a/OsmAnd/src/net/osmand/plus/importfiles/SettingsImportTask.java b/OsmAnd/src/net/osmand/plus/importfiles/SettingsImportTask.java index 55135832e5..d2d598d927 100644 --- a/OsmAnd/src/net/osmand/plus/importfiles/SettingsImportTask.java +++ b/OsmAnd/src/net/osmand/plus/importfiles/SettingsImportTask.java @@ -13,12 +13,12 @@ import net.osmand.FileUtils; import net.osmand.IndexConstants; import net.osmand.plus.CustomOsmandPlugin; import net.osmand.plus.R; -import net.osmand.plus.settings.backend.SettingsHelper.CheckDuplicatesListener; -import net.osmand.plus.settings.backend.SettingsHelper.PluginSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.ProfileSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsCollectListener; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsImportListener; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsHelper.CheckDuplicatesListener; +import net.osmand.plus.settings.backend.backup.PluginSettingsItem; +import net.osmand.plus.settings.backend.backup.ProfileSettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsHelper.SettingsCollectListener; +import net.osmand.plus.settings.backend.backup.SettingsHelper.SettingsImportListener; +import net.osmand.plus.settings.backend.backup.SettingsItem; import net.osmand.plus.settings.fragments.ImportSettingsFragment; import net.osmand.util.Algorithms; diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java index 272975b987..ac6b8287b4 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/MapDataMenuController.java @@ -22,6 +22,7 @@ import net.osmand.plus.activities.LocalIndexHelper; import net.osmand.plus.activities.LocalIndexHelper.LocalIndexType; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.PluginsFragment; import net.osmand.plus.download.DownloadActivityType; import net.osmand.plus.download.DownloadIndexesThread; import net.osmand.plus.download.DownloadValidationManager; @@ -110,8 +111,7 @@ public class MapDataMenuController extends MenuController { activity.getString(R.string.activate_srtm_plugin), Toast.LENGTH_LONG).show(); } } else { - activity.startActivity(new Intent(activity, activity.getMyApplication().getAppCustomization() - .getPluginsActivity())); + PluginsFragment.showInstance(activity.getSupportFragmentManager()); Toast.makeText(activity, activity.getString(R.string.activate_srtm_plugin), Toast.LENGTH_SHORT).show(); } diff --git a/OsmAnd/src/net/osmand/plus/monitoring/MonitoringSettingsFragment.java b/OsmAnd/src/net/osmand/plus/monitoring/MonitoringSettingsFragment.java index 901348a6d3..ae35fd767c 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/MonitoringSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/MonitoringSettingsFragment.java @@ -6,10 +6,13 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.SpannableString; import android.text.SpannableStringBuilder; +import android.view.LayoutInflater; +import android.view.View; import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; +import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.OsmandPlugin; @@ -32,6 +35,7 @@ import net.osmand.plus.widgets.style.CustomTypefaceSpan; import java.util.HashMap; import java.util.LinkedHashMap; +import static net.osmand.plus.activities.PluginInfoFragment.PLUGIN_INFO; import static net.osmand.plus.settings.backend.OsmandSettings.MONTHLY_DIRECTORY; import static net.osmand.plus.settings.backend.OsmandSettings.REC_DIRECTORY; import static net.osmand.plus.monitoring.OsmandMonitoringPlugin.MINUTES; @@ -46,6 +50,35 @@ public class MonitoringSettingsFragment extends BaseSettingsFragment private static final String OPEN_TRACKS = "open_tracks"; private static final String SAVE_GLOBAL_TRACK_INTERVAL = "save_global_track_interval"; + boolean showSwitchProfile = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Bundle args = getArguments(); + if (args != null) { + showSwitchProfile = args.getBoolean(PLUGIN_INFO, false); + } + } + + @Override + protected void createToolbar(LayoutInflater inflater, View view) { + super.createToolbar(inflater, view); + + View switchProfile = view.findViewById(R.id.profile_button); + if (switchProfile != null) { + AndroidUiHelper.updateVisibility(switchProfile, showSwitchProfile); + } + } + + @Override + public Bundle buildArguments() { + Bundle args = super.buildArguments(); + args.putBoolean(PLUGIN_INFO, showSwitchProfile); + return args; + } + @Override protected void setupPreferences() { setupSaveTrackToGpxPref(); diff --git a/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java b/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java index 94987d1c8a..ec255b1bbc 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java @@ -42,7 +42,7 @@ import net.osmand.plus.activities.SavingTrackHelper.SaveGpxResult; import net.osmand.plus.dashboard.tools.DashFragmentData; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.layers.MapInfoLayer; @@ -166,15 +166,9 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { public static final int[] MINUTES = new int[] {2, 3, 5}; public static final int[] MAX_INTERVAL_TO_SEND_MINUTES = new int[] {1, 2, 5, 10, 15, 20, 30, 60, 90, 2 * 60, 3 * 60, 4 * 60, 6 * 60, 12 * 60, 24 * 60}; - @Override - public Class getSettingsActivity() { - return SettingsMonitoringActivity.class; - } - - @Override - public Class getSettingsFragment() { - return MonitoringSettingsFragment.class; + public SettingsScreenType getSettingsScreenType() { + return SettingsScreenType.MONITORING_SETTINGS; } @Override diff --git a/OsmAnd/src/net/osmand/plus/monitoring/SettingsMonitoringActivity.java b/OsmAnd/src/net/osmand/plus/monitoring/SettingsMonitoringActivity.java deleted file mode 100644 index 7ace6377ad..0000000000 --- a/OsmAnd/src/net/osmand/plus/monitoring/SettingsMonitoringActivity.java +++ /dev/null @@ -1,325 +0,0 @@ -package net.osmand.plus.monitoring; - - -import android.content.BroadcastReceiver; -import android.content.DialogInterface; -import android.content.Intent; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceCategory; -import android.preference.PreferenceScreen; -import android.text.SpannableString; -import android.text.style.StyleSpan; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import net.osmand.plus.OsmAndFormatter; -import net.osmand.plus.OsmAndTaskManager.OsmAndTaskRunnable; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.R; -import net.osmand.plus.UiUtilities; -import net.osmand.plus.activities.SavingTrackHelper; -import net.osmand.plus.activities.SettingsBaseActivity; -import net.osmand.plus.settings.backend.ApplicationMode; -import net.osmand.util.Algorithms; - -import java.util.Map; - -import static net.osmand.plus.settings.backend.OsmandSettings.MONTHLY_DIRECTORY; -import static net.osmand.plus.settings.backend.OsmandSettings.REC_DIRECTORY; - -public class SettingsMonitoringActivity extends SettingsBaseActivity { - - public static final String PROFILE_STRING_KEY = "string_key"; - - private CheckBoxPreference routeServiceEnabled; - private BroadcastReceiver broadcastReceiver; - - public static final int[] BG_SECONDS = new int[]{0, 30, 60, 90}; - public static final int[] BG_MINUTES = new int[]{2, 3, 5, 10, 15, 30, 60, 90}; - private static final int[] SECONDS = OsmandMonitoringPlugin.SECONDS; - private static final int[] MINUTES = OsmandMonitoringPlugin.MINUTES; - private static final int[] MAX_INTERVAL_TO_SEND_MINUTES = OsmandMonitoringPlugin.MAX_INTERVAL_TO_SEND_MINUTES; - - public SettingsMonitoringActivity() { - super(true); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - ((OsmandApplication) getApplication()).applyTheme(this); - requestWindowFeature(Window.FEATURE_PROGRESS); - super.onCreate(savedInstanceState); - setProgressVisibility(false); - getToolbar().setTitle(R.string.monitoring_settings); - PreferenceScreen grp = getPreferenceScreen(); - - createLoggingSection(grp); - createLiveSection(grp); - createNotificationSection(grp); - - Intent intent = getIntent(); - if (intent != null && intent.hasExtra(PROFILE_STRING_KEY)) { - String modeName = intent.getStringExtra(PROFILE_STRING_KEY); - selectedAppMode = ApplicationMode.valueOfStringKey(modeName, ApplicationMode.CAR); - } else { - selectAppModeDialog().show(); - } - } - - - private void createLoggingSection(PreferenceScreen grp) { - PreferenceCategory cat = new PreferenceCategory(this); - cat.setTitle(R.string.save_track_to_gpx_globally); - grp.addPreference(cat); - - Preference globalrecord = new Preference(this); - globalrecord.setTitle(R.string.save_track_to_gpx_globally_headline); - globalrecord.setSummary(R.string.save_track_to_gpx_globally_descr); - globalrecord.setSelectable(false); - //setEnabled(false) creates bad readability on some devices - //globalrecord.setEnabled(false); - cat.addPreference(globalrecord); - - if(settings.SAVE_GLOBAL_TRACK_REMEMBER.get()) { - cat.addPreference(createTimeListPreference(settings.SAVE_GLOBAL_TRACK_INTERVAL, SECONDS, - MINUTES, 1000, settings.SAVE_GLOBAL_TRACK_REMEMBER, R.string.save_global_track_interval, R.string.save_global_track_interval_descr)); - } - - Preference pref = new Preference(this); - pref.setTitle(R.string.save_current_track); - pref.setSummary(getMyApplication().getString(R.string.save_current_track_descr) - + " (" + OsmAndFormatter.getFormattedDistance(getMyApplication().getSavingTrackHelper().getDistance(), getMyApplication()) + ")"); - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - SavingTrackHelper helper = getMyApplication().getSavingTrackHelper(); - if (helper.hasDataToSave()) { - saveCurrentTracks(helper); - } else { - helper.close(); - } - return true; - } - }); - cat.addPreference(pref); - - cat.addPreference(createCheckBoxPreference(settings.SAVE_TRACK_TO_GPX, R.string.save_track_to_gpx, - R.string.save_track_to_gpx_descrp)); - cat.addPreference(createTimeListPreference(settings.SAVE_TRACK_INTERVAL, SECONDS, - MINUTES, 1000, R.string.save_track_interval, R.string.save_track_interval_descr)); - String[] names; - Float[] floatValues; - floatValues = new Float[] {0.f, 2.0f, 5.0f, 10.0f, 20.0f, 30.0f, 50.0f}; - names = new String[floatValues.length]; - names[0] = getString(R.string.shared_string_not_selected); - for(int i = 1; i < floatValues.length; i++) { - names[i] = floatValues[i].intValue() + " " + getString(R.string.m); - } - cat.addPreference(createListPreference(settings.SAVE_TRACK_MIN_DISTANCE, names, floatValues, - R.string.save_track_min_distance, R.string.save_track_min_distance_descr)); - floatValues = new Float[] {0.f, 1.0f, 2.0f, 5.0f, 10.0f, 15.0f, 20.0f, 50.0f, 100.0f}; - names = new String[floatValues.length]; - names[0] = getString(R.string.shared_string_not_selected); - for(int i = 1; i < floatValues.length; i++) { - names[i] = floatValues[i].intValue() + " " + getString(R.string.m) + " (" + Math.round(floatValues[i]/0.3048f) + " " + getString(R.string.foot) + ")"; - } - cat.addPreference(createListPreference(settings.SAVE_TRACK_PRECISION, names, floatValues, - R.string.save_track_precision, R.string.save_track_precision_descr)); - floatValues = new Float[] {0.f, 0.000001f, 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f}; - names = new String[floatValues.length]; - names[0] = getString(R.string.shared_string_not_selected); - names[1] = "> 0"; // This option for the GPS chipset motion detection - for(int i = 2; i < floatValues.length; i++) { - names[i] = floatValues[i].intValue() + " " + getString(R.string.km_h); - floatValues[i] = floatValues[i] / 3.6f; - } - cat.addPreference(createListPreference(settings.SAVE_TRACK_MIN_SPEED, names, floatValues, - R.string.save_track_min_speed, R.string.save_track_min_speed_descr)); - cat.addPreference(createCheckBoxPreference(settings.AUTO_SPLIT_RECORDING, R.string.auto_split_recording_title, - R.string.auto_split_recording_descr)); - cat.addPreference(createCheckBoxPreference(settings.DISABLE_RECORDING_ONCE_APP_KILLED, R.string.disable_recording_once_app_killed, - R.string.disable_recording_once_app_killed_descrp)); - cat.addPreference(createCheckBoxPreference(settings.SAVE_HEADING_TO_GPX, R.string.save_heading, - R.string.save_heading_descr)); - - Integer[] intValues = new Integer[]{REC_DIRECTORY, MONTHLY_DIRECTORY}; - names = new String[intValues.length]; - names[0] = getString(R.string.store_tracks_in_rec_directory); - names[1] = getString(R.string.store_tracks_in_monthly_directories); -// names[2] = getString(R.string.store_tracks_in_daily_directories); - cat.addPreference(createListPreference(settings.TRACK_STORAGE_DIRECTORY, names, intValues, - R.string.track_storage_directory, R.string.track_storage_directory_descrp)); - } - - - private void createLiveSection(PreferenceScreen grp) { - PreferenceCategory cat; - cat = new PreferenceCategory(this); - cat.setTitle(R.string.live_monitoring_m); - grp.addPreference(cat); - - EditTextPreference urlPreference = createEditTextPreference(settings.LIVE_MONITORING_URL, R.string.live_monitoring_url, - R.string.live_monitoring_url_descr); - urlPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - if (Algorithms.isValidMessageFormat((String) newValue)) { - return SettingsMonitoringActivity.super.onPreferenceChange(preference, newValue); - } else { - Toast.makeText(SettingsMonitoringActivity.this, R.string.wrong_format, Toast.LENGTH_SHORT).show(); - return false; - } - } - }); - cat.addPreference(urlPreference); - final CheckBoxPreference liveMonitoring = createCheckBoxPreference(settings.LIVE_MONITORING, R.string.live_monitoring_m, - R.string.live_monitoring_m_descr); - cat.addPreference(liveMonitoring); - cat.addPreference(createTimeListPreference(settings.LIVE_MONITORING_INTERVAL, SECONDS, - MINUTES, 1000, R.string.live_monitoring_interval, R.string.live_monitoring_interval_descr)); - cat.addPreference(createTimeListPreference(settings.LIVE_MONITORING_MAX_INTERVAL_TO_SEND, null, - MAX_INTERVAL_TO_SEND_MINUTES, 1000, R.string.live_monitoring_max_interval_to_send, R.string.live_monitoring_max_interval_to_send_desrc)); - } - - private void createNotificationSection(PreferenceScreen grp) { - PreferenceCategory cat; - cat = new PreferenceCategory(this); - cat.setTitle(R.string.shared_string_notifications); - grp.addPreference(cat); - - final CheckBoxPreference tripRecording = createCheckBoxPreference(settings.SHOW_TRIP_REC_NOTIFICATION, R.string.trip_rec_notification_settings, - R.string.trip_rec_notification_settings_desc); - cat.addPreference(tripRecording); - } - - public void updateAllSettings() { - super.updateAllSettings(); - - if(routeServiceEnabled != null) { - routeServiceEnabled.setChecked(getMyApplication().getNavigationService() != null); - } - } - - private void saveCurrentTracks(final SavingTrackHelper helper) { - setProgressVisibility(true); - getMyApplication().getTaskManager().runInBackground(new OsmAndTaskRunnable() { - - @Override - protected Void doInBackground(Void... params) { - SavingTrackHelper helper = getMyApplication().getSavingTrackHelper(); - helper.saveDataToGpx(getMyApplication().getAppCustomization().getTracksDir()); - helper.close(); - return null; - } - @Override - protected void onPostExecute(Void result) { - setProgressVisibility(false); - } - - }, (Void) null); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if(broadcastReceiver != null) { - unregisterReceiver(broadcastReceiver); - broadcastReceiver = null; - } - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - String prefId = preference.getKey(); - if (preference instanceof ListPreference) { - int ind = ((ListPreference) preference).findIndexOfValue((String) newValue); - CharSequence entry = ((ListPreference) preference).getEntries()[ind]; - Map map = getListPrefValues().get(prefId); - if (map != null) { - newValue = map.get(entry); - } - } - showConfirmDialog(prefId, newValue); - return false; - } - - protected void showConfirmDialog(final String prefId, final Object newValue) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - - String appModeName = selectedAppMode.toHumanString(); - String currentModeText = getString(R.string.apply_to_current_profile, appModeName); - int start = currentModeText.indexOf(appModeName); - - SpannableString[] strings = new SpannableString[2]; - strings[0] = new SpannableString(getString(R.string.apply_to_all_profiles)); - strings[1] = new SpannableString(currentModeText); - strings[1].setSpan(new StyleSpan(Typeface.BOLD), start, start + appModeName.length(), 0); - - final int[] icons = new int[2]; - icons[0] = R.drawable.ic_action_copy; - icons[1] = selectedAppMode.getIconRes(); - - final boolean nightMode = !settings.isLightContent(); - final OsmandApplication app = getMyApplication(); - final LayoutInflater themedInflater = UiUtilities.getInflater(this, nightMode); - - //set up dialog title - View dialogTitle = themedInflater.inflate(R.layout.bottom_sheet_item_simple, null); - dialogTitle.findViewById(R.id.icon).setVisibility(View.GONE); - TextView tvTitle = dialogTitle.findViewById(R.id.title); - tvTitle.setText(R.string.change_default_settings); - int textSize = (int) app.getResources().getDimension(R.dimen.dialog_header_text_size); - tvTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); - builder.setCustomTitle(dialogTitle); - - final ArrayAdapter singleChoiceAdapter = new ArrayAdapter(this, R.layout.bottom_sheet_item_simple, R.id.title, strings) { - @NonNull - @Override - public View getView(int position, View convertView, @NonNull ViewGroup parent) { - View v = convertView; - if (v == null) { - v = themedInflater.inflate(R.layout.bottom_sheet_item_simple, parent, false); - } - int activeColor = nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light; - Drawable icon = app.getUIUtilities().getIcon(icons[position], activeColor); - ((TextView) v.findViewById(R.id.title)).setText(getItem(position)); - ((ImageView) v.findViewById(R.id.icon)).setImageDrawable(icon); - return v; - } - }; - - builder.setAdapter(singleChoiceAdapter, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - if (which == 0) { - settings.setPreferenceForAllModes(prefId, newValue); - } else { - settings.setPreference(prefId, newValue); - } - updateAllSettings(); - } - }); - - builder.setNegativeButton(R.string.discard_changes, null); - AlertDialog dialog = builder.create(); - dialog.getListView().setDividerHeight(0); - dialog.show(); - } -} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingFragment.java b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingFragment.java index 4c5ed73b07..daaeb6d034 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingFragment.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingFragment.java @@ -1,5 +1,6 @@ package net.osmand.plus.osmedit; +import android.content.Context; import android.content.Intent; import android.graphics.Typeface; import android.graphics.drawable.Drawable; @@ -7,31 +8,50 @@ import android.os.Bundle; import android.text.SpannableString; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.TextView; import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceViewHolder; -import net.osmand.plus.settings.backend.OsmAndAppCustomization; +import net.osmand.PlatformUtil; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; +import net.osmand.plus.osmedit.oauth.OsmOAuthAuthorizationAdapter; +import net.osmand.plus.settings.backend.OsmAndAppCustomization; +import net.osmand.plus.settings.bottomsheets.OsmLoginDataBottomSheet; import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.settings.fragments.OnPreferenceChanged; -import net.osmand.plus.settings.bottomsheets.OsmLoginDataBottomSheet; import net.osmand.plus.settings.preferences.SwitchPreferenceEx; import net.osmand.plus.widgets.style.CustomTypefaceSpan; +import org.apache.commons.logging.Log; + import static net.osmand.plus.myplaces.FavoritesActivity.TAB_ID; import static net.osmand.plus.osmedit.OsmEditingPlugin.OSM_EDIT_TAB; public class OsmEditingFragment extends BaseSettingsFragment implements OnPreferenceChanged { + private static final Log log = PlatformUtil.getLog(OsmEditingFragment.class); + private static final String OSM_EDITING_INFO = "osm_editing_info"; private static final String OPEN_OSM_EDITS = "open_osm_edits"; private static final String OSM_LOGIN_DATA = "osm_login_data"; + private static final String OSM_OAUTH_SUCCESS = "osm_oauth_success"; + private static final String OSM_OAUTH_CLEAR = "osm_oauth_clear"; + private static final String OSM_OAUTH_LOGIN = "osm_oauth_login"; + + private OsmOAuthAuthorizationAdapter client; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + client = new OsmOAuthAuthorizationAdapter(app); + } @Override protected void setupPreferences() { @@ -42,6 +62,7 @@ public class OsmEditingFragment extends BaseSettingsFragment implements OnPrefer setupOfflineEditingPref(); setupOsmEditsDescrPref(); setupOsmEditsPref(); + setupOAuthPrefs(); } @Override @@ -73,7 +94,7 @@ public class OsmEditingFragment extends BaseSettingsFragment implements OnPrefer Drawable enabled = getActiveIcon(R.drawable.ic_world_globe_dark); Drawable icon = getPersistentPrefIcon(enabled, disabled); - SwitchPreferenceEx offlineEditingPref = (SwitchPreferenceEx) findPreference(settings.OFFLINE_EDITION.getId()); + SwitchPreferenceEx offlineEditingPref = findPreference(settings.OFFLINE_EDITION.getId()); offlineEditingPref.setDescription(getString(R.string.offline_edition_descr)); offlineEditingPref.setIcon(icon); } @@ -101,9 +122,37 @@ public class OsmEditingFragment extends BaseSettingsFragment implements OnPrefer createProfile.setIcon(getActiveIcon(R.drawable.ic_action_folder)); } + private void setupOAuthPrefs() { + Context ctx = getContext(); + if (ctx != null) { + PreferenceScreen screen = getPreferenceScreen(); + if (client.isValidToken()) { + Preference prefOAuth = new Preference(ctx); + prefOAuth.setTitle(R.string.osm_authorization_success); + prefOAuth.setSummary(R.string.osm_authorization_success); + prefOAuth.setKey(OSM_OAUTH_SUCCESS); + + Preference prefClearToken = new Preference(ctx); + prefClearToken.setTitle(R.string.shared_string_logoff); + prefClearToken.setSummary(R.string.clear_osm_token); + prefClearToken.setKey(OSM_OAUTH_CLEAR); + + screen.addPreference(prefOAuth); + screen.addPreference(prefClearToken); + } else { + Preference prefOAuth = new Preference(ctx); + prefOAuth.setTitle(R.string.perform_oauth_authorization); + prefOAuth.setSummary(R.string.perform_oauth_authorization_description); + prefOAuth.setKey(OSM_OAUTH_LOGIN); + screen.addPreference(prefOAuth); + } + } + } + @Override public boolean onPreferenceClick(Preference preference) { - if (OPEN_OSM_EDITS.equals(preference.getKey())) { + String prefId = preference.getKey(); + if (OPEN_OSM_EDITS.equals(prefId)) { Bundle bundle = new Bundle(); bundle.putInt(TAB_ID, OSM_EDIT_TAB); @@ -113,12 +162,29 @@ public class OsmEditingFragment extends BaseSettingsFragment implements OnPrefer favorites.putExtra(MapActivity.INTENT_PARAMS, bundle); startActivity(favorites); return true; - } else if (OSM_LOGIN_DATA.equals(preference.getKey())) { + } else if (OSM_LOGIN_DATA.equals(prefId)) { FragmentManager fragmentManager = getFragmentManager(); if (fragmentManager != null) { OsmLoginDataBottomSheet.showInstance(fragmentManager, OSM_LOGIN_DATA, this, false, getSelectedAppMode()); return true; } + } else if (OSM_OAUTH_CLEAR.equals(prefId)) { + settings.USER_ACCESS_TOKEN.set(""); + settings.USER_ACCESS_TOKEN_SECRET.set(""); + + client.resetToken(); + client = new OsmOAuthAuthorizationAdapter(app); + + app.showShortToastMessage(R.string.osm_edit_logout_success); + updateAllSettings(); + return true; + } else if (OSM_OAUTH_LOGIN.equals(prefId)) { + View view = getView(); + if (view != null) { + ViewGroup appBarLayout = view.findViewById(R.id.appbar); + client.startOAuth(appBarLayout); + } + return true; } return super.onPreferenceClick(preference); } @@ -130,4 +196,11 @@ public class OsmEditingFragment extends BaseSettingsFragment implements OnPrefer nameAndPasswordPref.setSummary(settings.USER_NAME.get()); } } + + public void authorize(String oauthVerifier) { + if (client != null) { + client.authorize(oauthVerifier); + } + updateAllSettings(); + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingPlugin.java b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingPlugin.java index 5030530f88..ea779a93b1 100644 --- a/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingPlugin.java +++ b/OsmAnd/src/net/osmand/plus/osmedit/OsmEditingPlugin.java @@ -13,9 +13,11 @@ import android.widget.ArrayAdapter; import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; + import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; + import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; import net.osmand.data.Amenity; @@ -23,13 +25,11 @@ import net.osmand.data.MapObject; import net.osmand.data.TransportStop; import net.osmand.osm.PoiType; import net.osmand.osm.edit.Entity; -import net.osmand.plus.*; +import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuAdapter.ItemClickListener; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.settings.backend.OsmandPreference; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.activities.EnumAdapter; import net.osmand.plus.activities.EnumAdapter.IEnumWithResource; @@ -42,16 +42,21 @@ import net.osmand.plus.myplaces.AvailableGPXFragment.GpxInfo; import net.osmand.plus.myplaces.FavoritesActivity; import net.osmand.plus.osmedit.OsmPoint.Action; import net.osmand.plus.quickaction.QuickActionType; +import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.fragments.BaseSettingsFragment; +import net.osmand.plus.settings.fragments.BaseSettingsFragment.SettingsScreenType; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.util.Algorithms; + import org.apache.commons.logging.Log; import java.util.ArrayList; import java.util.List; -import static net.osmand.aidlapi.OsmAndCustomizationConstants.*; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_CREATE_POI; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.MAP_CONTEXT_MENU_OPEN_OSM_NOTE; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.OSM_EDITS; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.OSM_NOTES; import static net.osmand.plus.ContextMenuAdapter.makeDeleteAction; @@ -193,13 +198,8 @@ public class OsmEditingPlugin extends OsmandPlugin { } @Override - public Class getSettingsActivity() { - return SettingsOsmEditingActivity.class; - } - - @Override - public Class getSettingsFragment() { - return OsmEditingFragment.class; + public SettingsScreenType getSettingsScreenType() { + return SettingsScreenType.OPEN_STREET_MAP_EDITING; } @Override diff --git a/OsmAnd/src/net/osmand/plus/osmedit/SettingsOsmEditingActivity.java b/OsmAnd/src/net/osmand/plus/osmedit/SettingsOsmEditingActivity.java deleted file mode 100644 index 1be623825b..0000000000 --- a/OsmAnd/src/net/osmand/plus/osmedit/SettingsOsmEditingActivity.java +++ /dev/null @@ -1,189 +0,0 @@ -package net.osmand.plus.osmedit; - - -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.StrictMode; -import android.preference.CheckBoxPreference; -import android.preference.DialogPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceScreen; -import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.Toast; -import com.github.scribejava.core.model.OAuthAsyncRequestCallback; -import com.github.scribejava.core.model.Response; -import net.osmand.PlatformUtil; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.R; -import net.osmand.plus.activities.SettingsBaseActivity; -import net.osmand.plus.osmedit.oauth.OsmOAuthAuthorizationAdapter; -import net.osmand.plus.settings.backend.OsmAndAppCustomization; -import org.apache.commons.logging.Log; - -import java.io.IOException; - -public class SettingsOsmEditingActivity extends SettingsBaseActivity { - private OsmOAuthAuthorizationAdapter client; - private static final Log log = PlatformUtil.getLog(SettingsOsmEditingActivity.class); - - @Override - public void onCreate(Bundle savedInstanceState) { - StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() - .detectDiskReads() - .detectDiskWrites() - .detectNetwork() - .penaltyLog() - .build()); - - ((OsmandApplication) getApplication()).applyTheme(this); - super.onCreate(savedInstanceState); - - client = new OsmOAuthAuthorizationAdapter(getMyApplication()); - - getToolbar().setTitle(R.string.osm_settings); - @SuppressWarnings("deprecation") - PreferenceScreen grp = getPreferenceScreen(); - - DialogPreference loginDialogPreference = new OsmLoginDataDialogPreference(this, null); - grp.addPreference(loginDialogPreference); - - CheckBoxPreference poiEdit = createCheckBoxPreference(settings.OFFLINE_EDITION, - R.string.offline_edition, R.string.offline_edition_descr); - grp.addPreference(poiEdit); - - final Preference pref = new Preference(this); - pref.setTitle(R.string.local_openstreetmap_settings); - pref.setSummary(R.string.local_openstreetmap_settings_descr); - pref.setKey("local_openstreetmap_points"); - pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - OsmAndAppCustomization appCustomization = getMyApplication().getAppCustomization(); - final Intent favorites = new Intent(SettingsOsmEditingActivity.this, - appCustomization.getFavoritesActivity()); - favorites.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - getMyApplication().getSettings().FAVORITES_TAB.set(R.string.osm_edits); - startActivity(favorites); - return true; - } - }); - grp.addPreference(pref); - - final Preference prefOAuth = new Preference(this); - if (client.isValidToken()){ - prefOAuth.setTitle(R.string.osm_authorization_success); - prefOAuth.setSummary(R.string.osm_authorization_success); - prefOAuth.setKey("local_openstreetmap_oauth_success"); - final Preference prefClearToken = new Preference(this); - prefClearToken.setTitle(R.string.shared_string_logoff); - prefClearToken.setSummary(R.string.clear_osm_token); - prefClearToken.setKey("local_openstreetmap_oauth_clear"); - prefClearToken.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - settings.USER_ACCESS_TOKEN.set(""); - settings.USER_ACCESS_TOKEN_SECRET.set(""); - client.resetToken(); - Toast.makeText(SettingsOsmEditingActivity.this, R.string.osm_edit_logout_success, Toast.LENGTH_SHORT).show(); - finish(); - startActivity(getIntent()); - return true; - } - }); - grp.addPreference(prefClearToken); - } - else { - prefOAuth.setTitle(R.string.perform_oauth_authorization); - prefOAuth.setSummary(R.string.perform_oauth_authorization_description); - prefOAuth.setKey("local_openstreetmap_oauth_login"); - prefOAuth.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - ViewGroup preferenceView = (ViewGroup)getListView().getChildAt(preference.getOrder()); - client.startOAuth(preferenceView); - return true; - } - }); - } - grp.addPreference(prefOAuth); - } - - public class OsmLoginDataDialogPreference extends DialogPreference { - private TextView userNameEditText; - private TextView passwordEditText; - - public OsmLoginDataDialogPreference(Context context, AttributeSet attrs) { - super(context, attrs); - - setDialogLayoutResource(R.layout.osm_user_login_details); - setPositiveButtonText(android.R.string.ok); - setNegativeButtonText(android.R.string.cancel); - setDialogTitle(R.string.open_street_map_login_and_pass); - - setTitle(R.string.open_street_map_login_and_pass); - setSummary(R.string.open_street_map_login_descr); - - setDialogIcon(null); - } - - @Override - protected void onBindDialogView(View view) { - userNameEditText = (TextView) view.findViewById(R.id.user_name_field); - userNameEditText.setText(settings.USER_NAME.get()); - passwordEditText = (TextView) view.findViewById(R.id.password_field); - passwordEditText.setText(settings.USER_PASSWORD.get()); - super.onBindDialogView(view); - } - - @Override - protected void onDialogClosed(boolean positiveResult) { - if (positiveResult) { - settings.USER_NAME.set(userNameEditText.getText().toString()); - settings.USER_PASSWORD.set(passwordEditText.getText().toString()); - new ValidateOsmLoginDetailsTask(SettingsOsmEditingActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - } - - public static class ValidateOsmLoginDetailsTask extends AsyncTask { - private final Context context; - - public ValidateOsmLoginDetailsTask(Context context) { - this.context = context; - } - - @Override - protected OsmBugsUtil.OsmBugResult doInBackground(Void... params) { - OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); - assert plugin != null; - OsmBugsRemoteUtil remoteUtil = plugin.getOsmNotesRemoteUtil(); - return remoteUtil.validateLoginDetails(); - } - - @Override - protected void onPostExecute(OsmBugsUtil.OsmBugResult osmBugResult) { - String text = osmBugResult.warning != null ? osmBugResult.warning : context.getString(R.string.osm_authorization_success); - Toast.makeText(context, text, Toast.LENGTH_LONG).show(); - } - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - Uri uri = intent.getData(); - if (uri != null && uri.toString().startsWith("osmand-oauth")) { - String oauthVerifier = uri.getQueryParameter("oauth_verifier"); - client.authorize(oauthVerifier); - finish(); - startActivity(getIntent()); - } - } -} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/osmedit/ValidateOsmLoginDetailsTask.java b/OsmAnd/src/net/osmand/plus/osmedit/ValidateOsmLoginDetailsTask.java new file mode 100644 index 0000000000..722f104c51 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/osmedit/ValidateOsmLoginDetailsTask.java @@ -0,0 +1,34 @@ +package net.osmand.plus.osmedit; + +import android.os.AsyncTask; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.R; +import net.osmand.plus.osmedit.OsmBugsUtil.OsmBugResult; + +public class ValidateOsmLoginDetailsTask extends AsyncTask { + + private OsmandApplication app; + + public ValidateOsmLoginDetailsTask(OsmandApplication app) { + this.app = app; + } + + @Override + protected OsmBugResult doInBackground(Void... params) { + OsmEditingPlugin plugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class); + assert plugin != null; + OsmBugsRemoteUtil remoteUtil = plugin.getOsmNotesRemoteUtil(); + return remoteUtil.validateLoginDetails(); + } + + @Override + protected void onPostExecute(OsmBugResult osmBugResult) { + if (osmBugResult.warning != null) { + app.showToastMessage(osmBugResult.warning); + } else { + app.showToastMessage(R.string.osm_authorization_success); + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java b/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java index bda48d389f..bd7e8e9f4e 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java @@ -7,5 +7,7 @@ public enum ExportSettingsType { MAP_SOURCES, CUSTOM_RENDER_STYLE, CUSTOM_ROUTING, - AVOID_ROADS + AVOID_ROADS, + TRACKS, + MULTIMEDIA_NOTES } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmAndAppCustomization.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmAndAppCustomization.java index b915823810..7795f943ba 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmAndAppCustomization.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmAndAppCustomization.java @@ -27,12 +27,11 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.Version; import net.osmand.plus.activities.MapActivity; -import net.osmand.plus.activities.PluginsActivity; import net.osmand.plus.activities.SettingsActivity; import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.download.DownloadActivity; -import net.osmand.plus.importfiles.ImportHelper; import net.osmand.plus.helpers.WaypointHelper; +import net.osmand.plus.importfiles.ImportHelper; import net.osmand.plus.myplaces.FavoritesActivity; import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.views.OsmandMapTileView; @@ -195,10 +194,6 @@ public class OsmAndAppCustomization { return DownloadActivity.class; } - public Class getPluginsActivity() { - return PluginsActivity.class; - } - public Class getDownloadActivity() { return DownloadActivity.class; } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/SettingsHelper.java b/OsmAnd/src/net/osmand/plus/settings/backend/SettingsHelper.java deleted file mode 100644 index a708f92a46..0000000000 --- a/OsmAnd/src/net/osmand/plus/settings/backend/SettingsHelper.java +++ /dev/null @@ -1,3139 +0,0 @@ -package net.osmand.plus.settings.backend; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.AsyncTask; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -import net.osmand.AndroidUtils; -import net.osmand.IndexConstants; -import net.osmand.PlatformUtil; -import net.osmand.data.LatLon; -import net.osmand.map.ITileSource; -import net.osmand.map.TileSourceManager; -import net.osmand.map.TileSourceManager.TileSourceTemplate; -import net.osmand.map.WorldRegion; -import net.osmand.osm.MapPoiTypes; -import net.osmand.osm.PoiCategory; -import net.osmand.plus.CustomOsmandPlugin; -import net.osmand.plus.CustomOsmandPlugin.SuggestedDownloadItem; -import net.osmand.plus.CustomRegion; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.OsmandPlugin; -import net.osmand.plus.R; -import net.osmand.plus.SQLiteTileSource; -import net.osmand.plus.helpers.AvoidSpecificRoads; -import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; -import net.osmand.plus.poi.PoiUIFilter; -import net.osmand.plus.quickaction.QuickAction; -import net.osmand.plus.quickaction.QuickActionRegistry; -import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; -import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBuilder; -import net.osmand.router.GeneralRouter; -import net.osmand.util.Algorithms; - -import org.apache.commons.logging.Log; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; - -import static net.osmand.IndexConstants.OSMAND_SETTINGS_FILE_EXT; - -/* - Usage: - - SettingsHelper helper = app.getSettingsHelper(); - File file = new File(app.getAppPath(null), "settings.zip"); - - List items = new ArrayList<>(); - items.add(new GlobalSettingsItem(app.getSettings())); - items.add(new ProfileSettingsItem(app.getSettings(), ApplicationMode.DEFAULT)); - items.add(new ProfileSettingsItem(app.getSettings(), ApplicationMode.CAR)); - items.add(new ProfileSettingsItem(app.getSettings(), ApplicationMode.PEDESTRIAN)); - items.add(new ProfileSettingsItem(app.getSettings(), ApplicationMode.BICYCLE)); - items.add(new FileSettingsItem(app, new File(app.getAppPath(GPX_INDEX_DIR), "Day 2.gpx"))); - items.add(new FileSettingsItem(app, new File(app.getAppPath(GPX_INDEX_DIR), "Day 3.gpx"))); - items.add(new FileSettingsItem(app, new File(app.getAppPath(RENDERERS_DIR), "default.render.xml"))); - items.add(new DataSettingsItem(new byte[] {'t', 'e', 's', 't', '1'}, "data1")); - items.add(new DataSettingsItem(new byte[] {'t', 'e', 's', 't', '2'}, "data2")); - - helper.exportSettings(file, items); - - helper.importSettings(file); - */ - -public class SettingsHelper { - - public static final int VERSION = 1; - - public static final String SETTINGS_TYPE_LIST_KEY = "settings_type_list_key"; - public static final String REPLACE_KEY = "replace"; - public static final String SETTINGS_LATEST_CHANGES_KEY = "settings_latest_changes"; - public static final String SETTINGS_VERSION_KEY = "settings_version"; - - private static final Log LOG = PlatformUtil.getLog(SettingsHelper.class); - private static final int BUFFER = 1024; - - private OsmandApplication app; - - private ImportAsyncTask importTask; - private Map exportAsyncTasks = new HashMap<>(); - - public interface SettingsImportListener { - void onSettingsImportFinished(boolean succeed, @NonNull List items); - } - - public interface SettingsCollectListener { - void onSettingsCollectFinished(boolean succeed, boolean empty, @NonNull List items); - } - - public interface CheckDuplicatesListener { - void onDuplicatesChecked(@NonNull List duplicates, List items); - } - - public interface SettingsExportListener { - void onSettingsExportFinished(@NonNull File file, boolean succeed); - } - - public SettingsHelper(@NonNull OsmandApplication app) { - this.app = app; - } - - public enum SettingsItemType { - GLOBAL, - PROFILE, - PLUGIN, - DATA, - FILE, - RESOURCES, - QUICK_ACTIONS, - POI_UI_FILTERS, - MAP_SOURCES, - AVOID_ROADS, - SUGGESTED_DOWNLOADS, - DOWNLOADS - } - - public abstract static class SettingsItem { - - protected OsmandApplication app; - - protected String pluginId; - protected String fileName; - - boolean shouldReplace = false; - - protected List warnings; - - SettingsItem(@NonNull OsmandApplication app) { - this.app = app; - init(); - } - - SettingsItem(@NonNull OsmandApplication app, @Nullable SettingsItem baseItem) { - this.app = app; - if (baseItem != null) { - this.pluginId = baseItem.pluginId; - this.fileName = baseItem.fileName; - } - init(); - } - - SettingsItem(OsmandApplication app, @NonNull JSONObject json) throws JSONException { - this.app = app; - init(); - readFromJson(json); - } - - protected void init() { - warnings = new ArrayList<>(); - } - - public List getWarnings() { - return warnings; - } - - @NonNull - public abstract SettingsItemType getType(); - - @NonNull - public abstract String getName(); - - @NonNull - public abstract String getPublicName(@NonNull Context ctx); - - @NonNull - public String getDefaultFileName() { - return getName() + getDefaultFileExtension(); - } - - @NonNull - public String getDefaultFileExtension() { - return ".json"; - } - - public String getPluginId() { - return pluginId; - } - - @Nullable - public String getFileName() { - return fileName; - } - - public boolean applyFileName(@NonNull String fileName) { - String n = getFileName(); - return n != null && n.endsWith(fileName); - } - - public boolean shouldReadOnCollecting() { - return false; - } - - public void setShouldReplace(boolean shouldReplace) { - this.shouldReplace = shouldReplace; - } - - static SettingsItemType parseItemType(@NonNull JSONObject json) throws IllegalArgumentException, JSONException { - String type = json.has("type") ? json.getString("type") : null; - if (type == null) { - throw new IllegalArgumentException("No type field"); - } - if (type.equals("QUICK_ACTION")) { - type = "QUICK_ACTIONS"; - } - return SettingsItemType.valueOf(type); - } - - public boolean exists() { - return false; - } - - public void apply() { - // non implemented - } - - void readFromJson(@NonNull JSONObject json) throws JSONException { - pluginId = json.has("pluginId") ? json.getString("pluginId") : null; - if (json.has("name")) { - fileName = json.getString("name") + getDefaultFileExtension(); - } - if (json.has("file")) { - fileName = json.getString("file"); - } - readItemsFromJson(json); - } - - void writeToJson(@NonNull JSONObject json) throws JSONException { - json.put("type", getType().name()); - String pluginId = getPluginId(); - if (!Algorithms.isEmpty(pluginId)) { - json.put("pluginId", pluginId); - } - if (getWriter() != null) { - String fileName = getFileName(); - if (Algorithms.isEmpty(fileName)) { - fileName = getDefaultFileName(); - } - json.put("file", fileName); - } - writeItemsToJson(json); - } - - String toJson() throws JSONException { - JSONObject json = new JSONObject(); - writeToJson(json); - return json.toString(); - } - - void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { - // override - } - - void writeItemsToJson(@NonNull JSONObject json) { - // override - } - - @Nullable - abstract SettingsItemReader getReader(); - - @Nullable - abstract SettingsItemWriter getWriter(); - - @NonNull - SettingsItemReader getJsonReader() { - return new SettingsItemReader(this) { - @Override - public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { - StringBuilder buf = new StringBuilder(); - try { - BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); - String str; - while ((str = in.readLine()) != null) { - buf.append(str); - } - } catch (IOException e) { - throw new IOException("Cannot read json body", e); - } - String json = buf.toString(); - if (json.length() == 0) { - throw new IllegalArgumentException("Json body is empty"); - } - try { - readItemsFromJson(new JSONObject(json)); - } catch (JSONException e) { - throw new IllegalArgumentException("Json parsing error", e); - } - } - }; - } - - @NonNull - SettingsItemWriter getJsonWriter() { - return new SettingsItemWriter(this) { - @Override - public boolean writeToStream(@NonNull OutputStream outputStream) throws IOException { - JSONObject json = new JSONObject(); - writeItemsToJson(json); - if (json.length() > 0) { - try { - String s = json.toString(2); - outputStream.write(s.getBytes("UTF-8")); - } catch (JSONException e) { - LOG.error("Failed to write json to stream", e); - } - return true; - } - return false; - } - }; - } - - @Override - public int hashCode() { - return (getType().name() + getName()).hashCode(); - } - - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - if (!(other instanceof SettingsItem)) { - return false; - } - - SettingsItem item = (SettingsItem) other; - return item.getType() == getType() - && item.getName().equals(getName()) - && Algorithms.stringsEqual(item.getFileName(), getFileName()); - } - } - - public static class PluginSettingsItem extends SettingsItem { - - private CustomOsmandPlugin plugin; - private List pluginDependentItems; - - PluginSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - @Override - protected void init() { - super.init(); - pluginDependentItems = new ArrayList<>(); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.PLUGIN; - } - - @NonNull - @Override - public String getName() { - return plugin.getId(); - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return plugin.getName(); - } - - @NonNull - @Override - public String getDefaultFileName() { - return getName(); - } - - public CustomOsmandPlugin getPlugin() { - return plugin; - } - - public List getPluginDependentItems() { - return pluginDependentItems; - } - - @Override - public boolean exists() { - return OsmandPlugin.getPlugin(getPluginId()) != null; - } - - @Override - public void apply() { - if (shouldReplace || !exists()) { - for (SettingsHelper.SettingsItem item : pluginDependentItems) { - if (item instanceof SettingsHelper.FileSettingsItem) { - FileSettingsItem fileItem = (FileSettingsItem) item; - if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.RENDERING_STYLE) { - plugin.addRenderer(fileItem.getName()); - } else if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.ROUTING_CONFIG) { - plugin.addRouter(fileItem.getName()); - } else if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.OTHER) { - plugin.setResourceDirName(item.getFileName()); - } - } else if (item instanceof SuggestedDownloadsItem) { - plugin.updateSuggestedDownloads(((SuggestedDownloadsItem) item).getItems()); - } else if (item instanceof DownloadsItem) { - plugin.updateDownloadItems(((DownloadsItem) item).getItems()); - } - } - OsmandPlugin.addCustomPlugin(app, plugin); - } - } - - @Override - void readFromJson(@NonNull JSONObject json) throws JSONException { - super.readFromJson(json); - plugin = new CustomOsmandPlugin(app, json); - } - - @Override - void writeToJson(@NonNull JSONObject json) throws JSONException { - super.writeToJson(json); - plugin.writeAdditionalDataToJson(json); - } - - @Nullable - @Override - SettingsItemReader getReader() { - return null; - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return null; - } - } - - public static class SuggestedDownloadsItem extends SettingsItem { - - private List items; - - SuggestedDownloadsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - @Override - protected void init() { - super.init(); - items = new ArrayList<>(); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.SUGGESTED_DOWNLOADS; - - } - - @NonNull - @Override - public String getName() { - return "suggested_downloads"; - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return "suggested_downloads"; - } - - public List getItems() { - return items; - } - - @Override - void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { - try { - if (!json.has("items")) { - return; - } - JSONArray jsonArray = json.getJSONArray("items"); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject object = jsonArray.getJSONObject(i); - String scopeId = object.optString("scope-id"); - String searchType = object.optString("search-type"); - int limit = object.optInt("limit", -1); - - List names = new ArrayList<>(); - if (object.has("names")) { - JSONArray namesArray = object.getJSONArray("names"); - for (int j = 0; j < namesArray.length(); j++) { - names.add(namesArray.getString(j)); - } - } - SuggestedDownloadItem suggestedDownload = new SuggestedDownloadItem(scopeId, searchType, names, limit); - items.add(suggestedDownload); - } - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); - throw new IllegalArgumentException("Json parse error", e); - } - } - - @Override - void writeItemsToJson(@NonNull JSONObject json) { - JSONArray jsonArray = new JSONArray(); - if (!items.isEmpty()) { - try { - for (SuggestedDownloadItem downloadItem : items) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("scope-id", downloadItem.getScopeId()); - if (downloadItem.getLimit() != -1) { - jsonObject.put("limit", downloadItem.getLimit()); - } - if (!Algorithms.isEmpty(downloadItem.getSearchType())) { - jsonObject.put("search-type", downloadItem.getSearchType()); - } - if (!Algorithms.isEmpty(downloadItem.getNames())) { - JSONArray namesArray = new JSONArray(); - for (String downloadName : downloadItem.getNames()) { - namesArray.put(downloadName); - } - jsonObject.put("names", namesArray); - } - jsonArray.put(jsonObject); - } - json.put("items", jsonArray); - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); - LOG.error("Failed write to json", e); - } - } - } - - @Nullable - @Override - SettingsItemReader getReader() { - return null; - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return null; - } - } - - public static class DownloadsItem extends SettingsItem { - - private List items; - - DownloadsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - @Override - protected void init() { - super.init(); - items = new ArrayList<>(); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.DOWNLOADS; - - } - - @NonNull - @Override - public String getName() { - return "downloads"; - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return "downloads"; - } - - public List getItems() { - return items; - } - - @Override - void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { - try { - if (!json.has("items")) { - return; - } - JSONArray jsonArray = json.getJSONArray("items"); - items.addAll(CustomOsmandPlugin.collectRegionsFromJson(app, jsonArray)); - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); - throw new IllegalArgumentException("Json parse error", e); - } - } - - @Override - void writeItemsToJson(@NonNull JSONObject json) { - JSONArray jsonArray = new JSONArray(); - if (!items.isEmpty()) { - try { - for (WorldRegion region : items) { - if (region instanceof CustomRegion) { - JSONObject regionJson = ((CustomRegion) region).toJson(); - jsonArray.put(regionJson); - } - } - json.put("items", jsonArray); - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); - LOG.error("Failed write to json", e); - } - } - } - - @Nullable - @Override - SettingsItemReader getReader() { - return null; - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return null; - } - } - - public abstract static class CollectionSettingsItem extends SettingsItem { - - protected List items; - protected List appliedItems; - protected List duplicateItems; - protected List existingItems; - - @Override - protected void init() { - super.init(); - items = new ArrayList<>(); - appliedItems = new ArrayList<>(); - duplicateItems = new ArrayList<>(); - } - - CollectionSettingsItem(@NonNull OsmandApplication app, @Nullable CollectionSettingsItem baseItem, @NonNull List items) { - super(app, baseItem); - this.items = items; - } - - CollectionSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - @NonNull - public List getItems() { - return items; - } - - @NonNull - public List getAppliedItems() { - return appliedItems; - } - - @NonNull - public List getDuplicateItems() { - return duplicateItems; - } - - @NonNull - public List processDuplicateItems() { - if (!items.isEmpty()) { - for (T item : items) { - if (isDuplicate(item)) { - duplicateItems.add(item); - } - } - } - return duplicateItems; - } - - public List getNewItems() { - List res = new ArrayList<>(items); - res.removeAll(duplicateItems); - return res; - } - - public abstract boolean isDuplicate(@NonNull T item); - - @NonNull - public abstract T renameItem(@NonNull T item); - } - - public abstract static class SettingsItemReader { - - private T item; - - public SettingsItemReader(@NonNull T item) { - this.item = item; - } - - public abstract void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException; - } - - public abstract static class SettingsItemWriter { - - private T item; - - public SettingsItemWriter(T item) { - this.item = item; - } - - public T getItem() { - return item; - } - - public abstract boolean writeToStream(@NonNull OutputStream outputStream) throws IOException; - } - - public abstract static class OsmandSettingsItem extends SettingsItem { - - private OsmandSettings settings; - - protected OsmandSettingsItem(@NonNull OsmandSettings settings) { - super(settings.getContext()); - this.settings = settings; - } - - protected OsmandSettingsItem(@NonNull OsmandSettings settings, @Nullable OsmandSettingsItem baseItem) { - super(settings.getContext(), baseItem); - this.settings = settings; - } - - protected OsmandSettingsItem(@NonNull SettingsItemType type, @NonNull OsmandSettings settings, @NonNull JSONObject json) throws JSONException { - super(settings.getContext(), json); - this.settings = settings; - } - - public OsmandSettings getSettings() { - return settings; - } - } - - public abstract static class OsmandSettingsItemReader extends SettingsItemReader { - - private OsmandSettings settings; - - public OsmandSettingsItemReader(@NonNull T item, @NonNull OsmandSettings settings) { - super(item); - this.settings = settings; - } - - protected abstract void readPreferenceFromJson(@NonNull OsmandPreference preference, - @NonNull JSONObject json) throws JSONException; - - @Override - public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { - StringBuilder buf = new StringBuilder(); - try { - BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); - String str; - while ((str = in.readLine()) != null) { - buf.append(str); - } - } catch (IOException e) { - throw new IOException("Cannot read json body", e); - } - String jsonStr = buf.toString(); - if (Algorithms.isEmpty(jsonStr)) { - throw new IllegalArgumentException("Cannot find json body"); - } - final JSONObject json; - try { - json = new JSONObject(jsonStr); - } catch (JSONException e) { - throw new IllegalArgumentException("Json parse error", e); - } - readPreferencesFromJson(json); - } - - void readPreferencesFromJson(final JSONObject json) { - settings.getContext().runInUIThread(new Runnable() { - @Override - public void run() { - Map> prefs = settings.getRegisteredPreferences(); - Iterator iter = json.keys(); - while (iter.hasNext()) { - String key = iter.next(); - OsmandPreference p = prefs.get(key); - if (p != null) { - try { - readPreferenceFromJson(p, json); - } catch (Exception e) { - LOG.error("Failed to read preference: " + key, e); - } - } else { - LOG.warn("No preference while importing settings: " + key); - } - } - } - }); - } - } - - public abstract static class OsmandSettingsItemWriter extends SettingsItemWriter { - - private OsmandSettings settings; - - public OsmandSettingsItemWriter(@NonNull T item, @NonNull OsmandSettings settings) { - super(item); - this.settings = settings; - } - - protected abstract void writePreferenceToJson(@NonNull OsmandPreference preference, - @NonNull JSONObject json) throws JSONException; - - @Override - public boolean writeToStream(@NonNull OutputStream outputStream) throws IOException { - JSONObject json = new JSONObject(); - Map> prefs = settings.getRegisteredPreferences(); - for (OsmandPreference pref : prefs.values()) { - try { - writePreferenceToJson(pref, json); - } catch (JSONException e) { - LOG.error("Failed to write preference: " + pref.getId(), e); - } - } - if (json.length() > 0) { - try { - String s = json.toString(2); - outputStream.write(s.getBytes("UTF-8")); - } catch (JSONException e) { - LOG.error("Failed to write json to stream", e); - } - return true; - } - return false; - } - } - - public static class GlobalSettingsItem extends OsmandSettingsItem { - - public GlobalSettingsItem(@NonNull OsmandSettings settings) { - super(settings); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.GLOBAL; - } - - @NonNull - @Override - public String getName() { - return "general_settings"; - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return ctx.getString(R.string.general_settings_2); - } - - @Override - public boolean exists() { - return true; - } - - @Nullable - @Override - SettingsItemReader getReader() { - return new OsmandSettingsItemReader(this, getSettings()) { - @Override - protected void readPreferenceFromJson(@NonNull OsmandPreference preference, @NonNull JSONObject json) throws JSONException { - preference.readFromJson(json, null); - } - }; - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return new OsmandSettingsItemWriter(this, getSettings()) { - @Override - protected void writePreferenceToJson(@NonNull OsmandPreference preference, @NonNull JSONObject json) throws JSONException { - preference.writeToJson(json, null); - } - }; - } - } - - public static class ProfileSettingsItem extends OsmandSettingsItem { - - private ApplicationMode appMode; - private ApplicationModeBuilder builder; - private ApplicationModeBean modeBean; - - private JSONObject additionalPrefsJson; - private Set appModeBeanPrefsIds; - - public ProfileSettingsItem(@NonNull OsmandApplication app, @NonNull ApplicationMode appMode) { - super(app.getSettings()); - this.appMode = appMode; - } - - public ProfileSettingsItem(@NonNull OsmandApplication app, @Nullable ProfileSettingsItem baseItem, @NonNull ApplicationModeBean modeBean) { - super(app.getSettings(), baseItem); - this.modeBean = modeBean; - builder = ApplicationMode.fromModeBean(app, modeBean); - appMode = builder.getApplicationMode(); - } - - public ProfileSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(SettingsItemType.PROFILE, app.getSettings(), json); - } - - @Override - protected void init() { - super.init(); - appModeBeanPrefsIds = new HashSet<>(Arrays.asList(getAppModeBeanPrefsIds())); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.PROFILE; - } - - public ApplicationMode getAppMode() { - return appMode; - } - - public ApplicationModeBean getModeBean() { - return modeBean; - } - - @NonNull - @Override - public String getName() { - return appMode.getStringKey(); - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - if (appMode.isCustomProfile()) { - return modeBean.userProfileName; - } else if (appMode.getNameKeyResource() != -1) { - return ctx.getString(appMode.getNameKeyResource()); - } else { - return getName(); - } - } - - @NonNull - @Override - public String getDefaultFileName() { - return "profile_" + getName() + getDefaultFileExtension(); - } - - @Override - void readFromJson(@NonNull JSONObject json) throws JSONException { - super.readFromJson(json); - String appModeJson = json.getString("appMode"); - modeBean = ApplicationMode.fromJson(appModeJson); - builder = ApplicationMode.fromModeBean(app, modeBean); - ApplicationMode appMode = builder.getApplicationMode(); - if (!appMode.isCustomProfile()) { - appMode = ApplicationMode.valueOfStringKey(appMode.getStringKey(), appMode); - } - this.appMode = appMode; - } - - @Override - void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { - additionalPrefsJson = json.optJSONObject("prefs"); - } - - @Override - public boolean exists() { - return builder != null && ApplicationMode.valueOfStringKey(getName(), null) != null; - } - - private void renameProfile() { - List values = ApplicationMode.allPossibleValues(); - if (Algorithms.isEmpty(modeBean.userProfileName)) { - ApplicationMode appMode = ApplicationMode.valueOfStringKey(modeBean.stringKey, null); - if (appMode != null) { - modeBean.userProfileName = app.getString(appMode.getNameKeyResource()); - } - } - int number = 0; - while (true) { - number++; - String key = modeBean.stringKey + "_" + number; - String name = modeBean.userProfileName + '_' + number; - if (ApplicationMode.valueOfStringKey(key, null) == null && isNameUnique(values, name)) { - modeBean.userProfileName = name; - modeBean.stringKey = key; - break; - } - } - } - - private boolean isNameUnique(List values, String name) { - for (ApplicationMode mode : values) { - if (mode.getUserProfileName().equals(name)) { - return false; - } - } - return true; - } - - @Override - public void apply() { - if (!appMode.isCustomProfile() && !shouldReplace) { - ApplicationMode parent = ApplicationMode.valueOfStringKey(modeBean.stringKey, null); - renameProfile(); - ApplicationMode.ApplicationModeBuilder builder = ApplicationMode - .createCustomMode(parent, modeBean.stringKey, app) - .setIconResName(modeBean.iconName) - .setUserProfileName(modeBean.userProfileName) - .setRoutingProfile(modeBean.routingProfile) - .setRouteService(modeBean.routeService) - .setIconColor(modeBean.iconColor) - .setLocationIcon(modeBean.locIcon) - .setNavigationIcon(modeBean.navIcon); - app.getSettings().copyPreferencesFromProfile(parent, builder.getApplicationMode()); - appMode = ApplicationMode.saveProfile(builder, app); - } else if (!shouldReplace && exists()) { - renameProfile(); - builder = ApplicationMode.fromModeBean(app, modeBean); - appMode = ApplicationMode.saveProfile(builder, app); - } else { - builder = ApplicationMode.fromModeBean(app, modeBean); - appMode = ApplicationMode.saveProfile(builder, app); - } - ApplicationMode.changeProfileAvailability(appMode, true, app); - } - - public void applyAdditionalPrefs() { - if (additionalPrefsJson != null) { - updatePluginResPrefs(); - - SettingsItemReader reader = getReader(); - if (reader instanceof OsmandSettingsItemReader) { - ((OsmandSettingsItemReader) reader).readPreferencesFromJson(additionalPrefsJson); - } - } - } - - private void updatePluginResPrefs() { - String pluginId = getPluginId(); - if (Algorithms.isEmpty(pluginId)) { - return; - } - OsmandPlugin plugin = OsmandPlugin.getPlugin(pluginId); - if (plugin instanceof CustomOsmandPlugin) { - CustomOsmandPlugin customPlugin = (CustomOsmandPlugin) plugin; - String resDirPath = IndexConstants.PLUGINS_DIR + pluginId + "/" + customPlugin.getResourceDirName(); - - for (Iterator it = additionalPrefsJson.keys(); it.hasNext(); ) { - try { - String prefId = it.next(); - Object value = additionalPrefsJson.get(prefId); - if (value instanceof JSONObject) { - JSONObject jsonObject = (JSONObject) value; - for (Iterator iterator = jsonObject.keys(); iterator.hasNext(); ) { - String key = iterator.next(); - Object val = jsonObject.get(key); - if (val instanceof String) { - val = checkPluginResPath((String) val, resDirPath); - } - jsonObject.put(key, val); - } - } else if (value instanceof String) { - value = checkPluginResPath((String) value, resDirPath); - additionalPrefsJson.put(prefId, value); - } - } catch (JSONException e) { - LOG.error(e); - } - } - } - } - - private String checkPluginResPath(String path, String resDirPath) { - if (path.startsWith("@")) { - return resDirPath + "/" + path.substring(1); - } - return path; - } - - @Override - void writeToJson(@NonNull JSONObject json) throws JSONException { - super.writeToJson(json); - json.put("appMode", new JSONObject(appMode.toJson())); - } - - @Nullable - @Override - SettingsItemReader getReader() { - return new OsmandSettingsItemReader(this, getSettings()) { - @Override - protected void readPreferenceFromJson(@NonNull OsmandPreference preference, @NonNull JSONObject json) throws JSONException { - if (!appModeBeanPrefsIds.contains(preference.getId())) { - preference.readFromJson(json, appMode); - } - } - - @Override - void readPreferencesFromJson(final JSONObject json) { - getSettings().getContext().runInUIThread(new Runnable() { - @Override - public void run() { - OsmandSettings settings = getSettings(); - Map> prefs = settings.getRegisteredPreferences(); - Iterator iter = json.keys(); - while (iter.hasNext()) { - String key = iter.next(); - OsmandPreference p = prefs.get(key); - if (p == null) { - if (OsmandSettings.isRoutingPreference(key)) { - p = settings.registerStringPreference(key, ""); - } - } - if (p != null) { - try { - readPreferenceFromJson(p, json); - if (OsmandSettings.isRoutingPreference(p.getId())) { - if (p.getId().endsWith(GeneralRouter.USE_SHORTEST_WAY)) { - settings.FAST_ROUTE_MODE.setModeValue(appMode, - !settings.getCustomRoutingBooleanProperty(GeneralRouter.USE_SHORTEST_WAY, false).getModeValue(appMode)); - } - } - } catch (Exception e) { - LOG.error("Failed to read preference: " + key, e); - } - } else { - LOG.warn("No preference while importing settings: " + key); - } - } - } - }); - } - }; - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return new OsmandSettingsItemWriter(this, getSettings()) { - @Override - protected void writePreferenceToJson(@NonNull OsmandPreference preference, @NonNull JSONObject json) throws JSONException { - if (!appModeBeanPrefsIds.contains(preference.getId())) { - preference.writeToJson(json, appMode); - } - } - }; - } - - private String[] getAppModeBeanPrefsIds() { - OsmandSettings settings = app.getSettings(); - return new String[] { - settings.ICON_COLOR.getId(), - settings.ICON_RES_NAME.getId(), - settings.PARENT_APP_MODE.getId(), - settings.ROUTING_PROFILE.getId(), - settings.ROUTE_SERVICE.getId(), - settings.USER_PROFILE_NAME.getId(), - settings.LOCATION_ICON.getId(), - settings.NAVIGATION_ICON.getId(), - settings.APP_MODE_ORDER.getId() - }; - } - } - - public abstract static class StreamSettingsItemReader extends SettingsItemReader { - - public StreamSettingsItemReader(@NonNull StreamSettingsItem item) { - super(item); - } - } - - public static class StreamSettingsItemWriter extends SettingsItemWriter { - - public StreamSettingsItemWriter(StreamSettingsItem item) { - super(item); - } - - @Override - public boolean writeToStream(@NonNull OutputStream outputStream) throws IOException { - boolean hasData = false; - InputStream is = getItem().inputStream; - if (is != null) { - byte[] data = new byte[BUFFER]; - int count; - while ((count = is.read(data, 0, BUFFER)) != -1) { - outputStream.write(data, 0, count); - if (!hasData) { - hasData = true; - } - } - Algorithms.closeStream(is); - } - return hasData; - } - } - - public abstract static class StreamSettingsItem extends SettingsItem { - - @Nullable - private InputStream inputStream; - protected String name; - - public StreamSettingsItem(@NonNull OsmandApplication app, @NonNull String name) { - super(app); - this.name = name; - this.fileName = name; - } - - StreamSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - public StreamSettingsItem(@NonNull OsmandApplication app, @NonNull InputStream inputStream, @NonNull String name) { - super(app); - this.inputStream = inputStream; - this.name = name; - this.fileName = name; - } - - @Nullable - public InputStream getInputStream() { - return inputStream; - } - - protected void setInputStream(@Nullable InputStream inputStream) { - this.inputStream = inputStream; - } - - @NonNull - @Override - public String getName() { - return name; - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return getName(); - } - - @NonNull - @Override - public String getDefaultFileExtension() { - return ""; - } - - @Override - void readFromJson(@NonNull JSONObject json) throws JSONException { - super.readFromJson(json); - name = json.has("name") ? json.getString("name") : null; - } - - @Nullable - @Override - public SettingsItemWriter getWriter() { - return new StreamSettingsItemWriter(this); - } - } - - public static class DataSettingsItem extends StreamSettingsItem { - - @Nullable - private byte[] data; - - public DataSettingsItem(@NonNull OsmandApplication app, @NonNull String name) { - super(app, name); - } - - DataSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - public DataSettingsItem(@NonNull OsmandApplication app, @NonNull byte[] data, @NonNull String name) { - super(app, name); - this.data = data; - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.DATA; - } - - @NonNull - @Override - public String getDefaultFileExtension() { - return ".dat"; - } - - @Nullable - public byte[] getData() { - return data; - } - - @Override - void readFromJson(@NonNull JSONObject json) throws JSONException { - super.readFromJson(json); - String fileName = getFileName(); - if (!Algorithms.isEmpty(fileName)) { - name = Algorithms.getFileNameWithoutExtension(new File(fileName)); - } - } - - @Nullable - @Override - SettingsItemReader getReader() { - return new StreamSettingsItemReader(this) { - @Override - public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - int nRead; - byte[] data = new byte[BUFFER]; - while ((nRead = inputStream.read(data, 0, data.length)) != -1) { - buffer.write(data, 0, nRead); - } - - buffer.flush(); - DataSettingsItem.this.data = buffer.toByteArray(); - } - }; - } - - @Nullable - @Override - public SettingsItemWriter getWriter() { - setInputStream(new ByteArrayInputStream(data)); - return super.getWriter(); - } - } - - public static class FileSettingsItem extends StreamSettingsItem { - - public enum FileSubtype { - UNKNOWN("", null), - OTHER("other", ""), - ROUTING_CONFIG("routing_config", IndexConstants.ROUTING_PROFILES_DIR), - RENDERING_STYLE("rendering_style", IndexConstants.RENDERERS_DIR), - OBF_MAP("obf_map", IndexConstants.MAPS_PATH), - TILES_MAP("tiles_map", IndexConstants.TILES_INDEX_DIR), - GPX("gpx", IndexConstants.GPX_INDEX_DIR), - VOICE("voice", IndexConstants.VOICE_INDEX_DIR), - TRAVEL("travel", IndexConstants.WIKIVOYAGE_INDEX_DIR); - - private String subtypeName; - private String subtypeFolder; - - FileSubtype(String subtypeName, String subtypeFolder) { - this.subtypeName = subtypeName; - this.subtypeFolder = subtypeFolder; - } - - public String getSubtypeName() { - return subtypeName; - } - - public String getSubtypeFolder() { - return subtypeFolder; - } - - public static FileSubtype getSubtypeByName(@NonNull String name) { - for (FileSubtype subtype : FileSubtype.values()) { - if (name.equals(subtype.subtypeName)) { - return subtype; - } - } - return null; - } - - public static FileSubtype getSubtypeByFileName(@NonNull String fileName) { - String name = fileName; - if (fileName.startsWith(File.separator)) { - name = fileName.substring(1); - } - for (FileSubtype subtype : FileSubtype.values()) { - switch (subtype) { - case UNKNOWN: - case OTHER: - break; - case OBF_MAP: - if (name.endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { - return subtype; - } - break; - default: - if (name.startsWith(subtype.subtypeFolder)) { - return subtype; - } - break; - } - } - return UNKNOWN; - } - - @NonNull - @Override - public String toString() { - return subtypeName; - } - } - - protected File file; - private File appPath; - protected FileSubtype subtype; - - public FileSettingsItem(@NonNull OsmandApplication app, @NonNull File file) throws IllegalArgumentException { - super(app, file.getPath().replace(app.getAppPath(null).getPath(), "")); - this.file = file; - this.appPath = app.getAppPath(null); - String fileName = getFileName(); - if (fileName != null) { - this.subtype = FileSubtype.getSubtypeByFileName(fileName); - } - if (subtype == FileSubtype.UNKNOWN || subtype == null) { - throw new IllegalArgumentException("Unknown file subtype: " + fileName); - } - } - - FileSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - this.appPath = app.getAppPath(null); - if (subtype == FileSubtype.OTHER) { - this.file = new File(appPath, name); - } else if (subtype == FileSubtype.UNKNOWN || subtype == null) { - throw new IllegalArgumentException("Unknown file subtype: " + getFileName()); - } else { - this.file = new File(app.getAppPath(subtype.subtypeFolder), name); - } - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.FILE; - } - - public File getPluginPath() { - String pluginId = getPluginId(); - if (!Algorithms.isEmpty(pluginId)) { - return new File(appPath, IndexConstants.PLUGINS_DIR + pluginId); - } - return appPath; - } - - @Override - void readFromJson(@NonNull JSONObject json) throws JSONException { - super.readFromJson(json); - String fileName = getFileName(); - if (subtype == null) { - String subtypeStr = json.has("subtype") ? json.getString("subtype") : null; - if (!Algorithms.isEmpty(subtypeStr)) { - subtype = FileSubtype.getSubtypeByName(subtypeStr); - } else if (!Algorithms.isEmpty(fileName)) { - subtype = FileSubtype.getSubtypeByFileName(fileName); - } else { - subtype = FileSubtype.UNKNOWN; - } - } - if (!Algorithms.isEmpty(fileName)) { - if (subtype == FileSubtype.OTHER) { - name = fileName; - } else if (subtype != null && subtype != FileSubtype.UNKNOWN) { - name = Algorithms.getFileWithoutDirs(fileName); - } - } - } - - @Override - void writeToJson(@NonNull JSONObject json) throws JSONException { - super.writeToJson(json); - if (subtype != null) { - json.put("subtype", subtype.getSubtypeName()); - } - } - - @NonNull - public File getFile() { - return file; - } - - @NonNull - public FileSubtype getSubtype() { - return subtype; - } - - @Override - public boolean exists() { - return file.exists(); - } - - private File renameFile(File file) { - int number = 0; - String path = file.getAbsolutePath(); - while (true) { - number++; - String copyName = path.replaceAll(file.getName(), file.getName().replaceFirst("[.]", "_" + number + ".")); - File copyFile = new File(copyName); - if (!copyFile.exists()) { - return copyFile; - } - } - } - - @Nullable - @Override - SettingsItemReader getReader() { - return new StreamSettingsItemReader(this) { - @Override - public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { - OutputStream output; - File dest = FileSettingsItem.this.file; - if (dest.exists() && !shouldReplace) { - dest = renameFile(dest); - } - if (dest.getParentFile() != null && !dest.getParentFile().exists()) { - dest.getParentFile().mkdirs(); - } - output = new FileOutputStream(dest); - byte[] buffer = new byte[BUFFER]; - int count; - try { - while ((count = inputStream.read(buffer)) != -1) { - output.write(buffer, 0, count); - } - output.flush(); - } finally { - Algorithms.closeStream(output); - } - } - }; - } - - @Nullable - @Override - public SettingsItemWriter getWriter() { - try { - setInputStream(new FileInputStream(file)); - } catch (FileNotFoundException e) { - warnings.add(app.getString(R.string.settings_item_read_error, file.getName())); - LOG.error("Failed to set input stream from file: " + file.getName(), e); - } - return super.getWriter(); - } - } - - public static class ResourcesSettingsItem extends FileSettingsItem { - - ResourcesSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - shouldReplace = true; - String fileName = getFileName(); - if (!Algorithms.isEmpty(fileName) && !fileName.endsWith(File.separator)) { - this.fileName = fileName + File.separator; - } - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.RESOURCES; - } - - @Override - void readFromJson(@NonNull JSONObject json) throws JSONException { - subtype = FileSubtype.OTHER; - super.readFromJson(json); - } - - @Override - void writeToJson(@NonNull JSONObject json) throws JSONException { - super.writeToJson(json); - String fileName = getFileName(); - if (!Algorithms.isEmpty(fileName)) { - if (fileName.endsWith(File.separator)) { - fileName = fileName.substring(0, fileName.length() - 1); - } - json.put("file", fileName); - } - } - - @Override - public boolean applyFileName(@NonNull String fileName) { - if (fileName.endsWith(File.separator)) { - return false; - } - String itemFileName = getFileName(); - if (itemFileName != null && itemFileName.endsWith(File.separator)) { - if (fileName.startsWith(itemFileName)) { - this.file = new File(getPluginPath(), fileName); - return true; - } else { - return false; - } - } else { - return super.applyFileName(fileName); - } - } - - @Nullable - @Override - public SettingsItemWriter getWriter() { - return null; - } - } - - public static class QuickActionsSettingsItem extends CollectionSettingsItem { - - private QuickActionRegistry actionRegistry; - - public QuickActionsSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { - super(app, null, items); - } - - public QuickActionsSettingsItem(@NonNull OsmandApplication app, @Nullable QuickActionsSettingsItem baseItem, @NonNull List items) { - super(app, baseItem, items); - } - - QuickActionsSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - @Override - protected void init() { - super.init(); - actionRegistry = app.getQuickActionRegistry(); - existingItems = actionRegistry.getQuickActions(); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.QUICK_ACTIONS; - } - - @Override - public boolean isDuplicate(@NonNull QuickAction item) { - return !actionRegistry.isNameUnique(item, app); - } - - @NonNull - @Override - public QuickAction renameItem(@NonNull QuickAction item) { - return actionRegistry.generateUniqueName(item, app); - } - - @Override - public void apply() { - List newItems = getNewItems(); - if (!newItems.isEmpty() || !duplicateItems.isEmpty()) { - appliedItems = new ArrayList<>(newItems); - List newActions = new ArrayList<>(existingItems); - if (!duplicateItems.isEmpty()) { - if (shouldReplace) { - for (QuickAction duplicateItem : duplicateItems) { - for (QuickAction savedAction : existingItems) { - if (duplicateItem.getName(app).equals(savedAction.getName(app))) { - newActions.remove(savedAction); - } - } - } - } else { - for (QuickAction duplicateItem : duplicateItems) { - renameItem(duplicateItem); - } - } - appliedItems.addAll(duplicateItems); - } - newActions.addAll(appliedItems); - actionRegistry.updateQuickActions(newActions); - } - } - - @Override - public boolean shouldReadOnCollecting() { - return true; - } - - @NonNull - @Override - public String getName() { - return "quick_actions"; - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return "quick_actions"; - } - - @Override - void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { - try { - if (!json.has("items")) { - return; - } - Gson gson = new Gson(); - Type type = new TypeToken>() { - }.getType(); - QuickActionRegistry quickActionRegistry = app.getQuickActionRegistry(); - JSONArray itemsJson = json.getJSONArray("items"); - for (int i = 0; i < itemsJson.length(); i++) { - JSONObject object = itemsJson.getJSONObject(i); - String name = object.getString("name"); - QuickAction quickAction = null; - if (object.has("actionType")) { - quickAction = quickActionRegistry.newActionByStringType(object.getString("actionType")); - } else if (object.has("type")) { - quickAction = quickActionRegistry.newActionByType(object.getInt("type")); - } - if (quickAction != null) { - String paramsString = object.getString("params"); - HashMap params = gson.fromJson(paramsString, type); - - if (!name.isEmpty()) { - quickAction.setName(name); - } - quickAction.setParams(params); - items.add(quickAction); - } else { - warnings.add(app.getString(R.string.settings_item_read_error, name)); - } - } - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); - throw new IllegalArgumentException("Json parse error", e); - } - } - - @Override - void writeItemsToJson(@NonNull JSONObject json) { - JSONArray jsonArray = new JSONArray(); - Gson gson = new Gson(); - Type type = new TypeToken>() { - }.getType(); - if (!items.isEmpty()) { - try { - for (QuickAction action : items) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("name", action.hasCustomName(app) - ? action.getName(app) : ""); - jsonObject.put("actionType", action.getActionType().getStringId()); - jsonObject.put("params", gson.toJson(action.getParams(), type)); - jsonArray.put(jsonObject); - } - json.put("items", jsonArray); - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); - LOG.error("Failed write to json", e); - } - } - } - - @Nullable - @Override - SettingsItemReader getReader() { - return getJsonReader(); - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return null; - } - } - - public static class PoiUiFiltersSettingsItem extends CollectionSettingsItem { - - public PoiUiFiltersSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { - super(app, null, items); - } - - public PoiUiFiltersSettingsItem(@NonNull OsmandApplication app, @Nullable PoiUiFiltersSettingsItem baseItem, @NonNull List items) { - super(app, baseItem, items); - } - - PoiUiFiltersSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - @Override - protected void init() { - super.init(); - existingItems = app.getPoiFilters().getUserDefinedPoiFilters(false); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.POI_UI_FILTERS; - } - - @Override - public void apply() { - List newItems = getNewItems(); - if (!newItems.isEmpty() || !duplicateItems.isEmpty()) { - appliedItems = new ArrayList<>(newItems); - - for (PoiUIFilter duplicate : duplicateItems) { - appliedItems.add(shouldReplace ? duplicate : renameItem(duplicate)); - } - for (PoiUIFilter filter : appliedItems) { - app.getPoiFilters().createPoiFilter(filter, false); - } - app.getSearchUICore().refreshCustomPoiFilters(); - } - } - - @Override - public boolean isDuplicate(@NonNull PoiUIFilter item) { - String savedName = item.getName(); - for (PoiUIFilter filter : existingItems) { - if (filter.getName().equals(savedName)) { - return true; - } - } - return false; - } - - @NonNull - @Override - public PoiUIFilter renameItem(@NonNull PoiUIFilter item) { - int number = 0; - while (true) { - number++; - PoiUIFilter renamedItem = new PoiUIFilter(item, - item.getName() + "_" + number, - item.getFilterId() + "_" + number); - if (!isDuplicate(renamedItem)) { - return renamedItem; - } - } - } - - @NonNull - @Override - public String getName() { - return "poi_ui_filters"; - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return "poi_ui_filters"; - } - - @Override - public boolean shouldReadOnCollecting() { - return true; - } - - @Override - void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { - try { - if (!json.has("items")) { - return; - } - JSONArray jsonArray = json.getJSONArray("items"); - Gson gson = new Gson(); - Type type = new TypeToken>>() { - }.getType(); - MapPoiTypes poiTypes = app.getPoiTypes(); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject object = jsonArray.getJSONObject(i); - String name = object.getString("name"); - String filterId = object.getString("filterId"); - String acceptedTypesString = object.getString("acceptedTypes"); - HashMap> acceptedTypes = gson.fromJson(acceptedTypesString, type); - Map> acceptedTypesDone = new HashMap<>(); - for (Map.Entry> mapItem : acceptedTypes.entrySet()) { - final PoiCategory a = poiTypes.getPoiCategoryByName(mapItem.getKey()); - acceptedTypesDone.put(a, mapItem.getValue()); - } - PoiUIFilter filter = new PoiUIFilter(name, filterId, acceptedTypesDone, app); - items.add(filter); - } - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); - throw new IllegalArgumentException("Json parse error", e); - } - } - - @Override - void writeItemsToJson(@NonNull JSONObject json) { - JSONArray jsonArray = new JSONArray(); - Gson gson = new Gson(); - Type type = new TypeToken>>() { - }.getType(); - if (!items.isEmpty()) { - try { - for (PoiUIFilter filter : items) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("name", filter.getName()); - jsonObject.put("filterId", filter.getFilterId()); - jsonObject.put("acceptedTypes", gson.toJson(filter.getAcceptedTypes(), type)); - jsonArray.put(jsonObject); - } - json.put("items", jsonArray); - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); - LOG.error("Failed write to json", e); - } - } - } - - @Nullable - @Override - SettingsItemReader getReader() { - return getJsonReader(); - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return null; - } - } - - public static class MapSourcesSettingsItem extends CollectionSettingsItem { - - private List existingItemsNames; - - public MapSourcesSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { - super(app, null, items); - } - - public MapSourcesSettingsItem(@NonNull OsmandApplication app, @Nullable MapSourcesSettingsItem baseItem, @NonNull List items) { - super(app, baseItem, items); - } - - MapSourcesSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - @Override - protected void init() { - super.init(); - existingItemsNames = new ArrayList<>(app.getSettings().getTileSourceEntries().values()); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.MAP_SOURCES; - } - - @Override - public void apply() { - List newItems = getNewItems(); - if (!newItems.isEmpty() || !duplicateItems.isEmpty()) { - appliedItems = new ArrayList<>(newItems); - if (shouldReplace) { - for (ITileSource tileSource : duplicateItems) { - if (tileSource instanceof SQLiteTileSource) { - File f = app.getAppPath(IndexConstants.TILES_INDEX_DIR + tileSource.getName() + IndexConstants.SQLITE_EXT); - if (f != null && f.exists() && Algorithms.removeAllFiles(f)) { - appliedItems.add(tileSource); - } - } else if (tileSource instanceof TileSourceManager.TileSourceTemplate) { - File f = app.getAppPath(IndexConstants.TILES_INDEX_DIR + tileSource.getName()); - if (f != null && f.exists() && f.isDirectory() && Algorithms.removeAllFiles(f)) { - appliedItems.add(tileSource); - } - } - } - } else { - for (ITileSource tileSource : duplicateItems) { - appliedItems.add(renameItem(tileSource)); - } - } - for (ITileSource tileSource : appliedItems) { - if (tileSource instanceof TileSourceManager.TileSourceTemplate) { - app.getSettings().installTileSource((TileSourceManager.TileSourceTemplate) tileSource); - } else if (tileSource instanceof SQLiteTileSource) { - ((SQLiteTileSource) tileSource).createDataBase(); - } - } - } - } - - @NonNull - @Override - public ITileSource renameItem(@NonNull ITileSource item) { - int number = 0; - while (true) { - number++; - if (item instanceof SQLiteTileSource) { - SQLiteTileSource oldItem = (SQLiteTileSource) item; - SQLiteTileSource renamedItem = new SQLiteTileSource( - oldItem, - oldItem.getName() + "_" + number, - app); - if (!isDuplicate(renamedItem)) { - return renamedItem; - } - } else if (item instanceof TileSourceManager.TileSourceTemplate) { - TileSourceManager.TileSourceTemplate oldItem = (TileSourceManager.TileSourceTemplate) item; - oldItem.setName(oldItem.getName() + "_" + number); - if (!isDuplicate(oldItem)) { - return oldItem; - } - } - } - } - - @Override - public boolean isDuplicate(@NonNull ITileSource item) { - for (String name : existingItemsNames) { - if (name.equals(item.getName())) { - return true; - } - } - return false; - } - - @NonNull - @Override - public String getName() { - return "map_sources"; - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return "map_sources"; - } - - @Override - public boolean shouldReadOnCollecting() { - return true; - } - - @Override - void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { - try { - if (!json.has("items")) { - return; - } - JSONArray jsonArray = json.getJSONArray("items"); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject object = jsonArray.getJSONObject(i); - boolean sql = object.optBoolean("sql"); - String name = object.optString("name"); - int minZoom = object.optInt("minZoom"); - int maxZoom = object.optInt("maxZoom"); - String url = object.optString("url"); - String randoms = object.optString("randoms"); - boolean ellipsoid = object.optBoolean("ellipsoid", false); - boolean invertedY = object.optBoolean("inverted_y", false); - String referer = object.optString("referer"); - String userAgent = object.optString("userAgent"); - boolean timeSupported = object.optBoolean("timesupported", false); - long expire = object.optLong("expire", -1); - boolean inversiveZoom = object.optBoolean("inversiveZoom", false); - String ext = object.optString("ext"); - int tileSize = object.optInt("tileSize"); - int bitDensity = object.optInt("bitDensity"); - int avgSize = object.optInt("avgSize"); - String rule = object.optString("rule"); - - if (expire > 0 && expire < 3600000) { - expire = expire * 60 * 1000L; - } - - ITileSource template; - if (!sql) { - TileSourceTemplate tileSourceTemplate = new TileSourceTemplate(name, url, ext, maxZoom, minZoom, tileSize, bitDensity, avgSize); - tileSourceTemplate.setRule(rule); - tileSourceTemplate.setRandoms(randoms); - tileSourceTemplate.setReferer(referer); - tileSourceTemplate.setUserAgent(userAgent); - tileSourceTemplate.setEllipticYTile(ellipsoid); - tileSourceTemplate.setInvertedYTile(invertedY); - tileSourceTemplate.setExpirationTimeMillis(timeSupported ? expire : -1); - - template = tileSourceTemplate; - } else { - template = new SQLiteTileSource(app, name, minZoom, maxZoom, url, randoms, ellipsoid, invertedY, referer, userAgent, timeSupported, expire, inversiveZoom, rule); - } - items.add(template); - } - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); - throw new IllegalArgumentException("Json parse error", e); - } - } - - @Override - void writeItemsToJson(@NonNull JSONObject json) { - JSONArray jsonArray = new JSONArray(); - if (!items.isEmpty()) { - try { - for (ITileSource template : items) { - JSONObject jsonObject = new JSONObject(); - boolean sql = template instanceof SQLiteTileSource; - jsonObject.put("sql", sql); - jsonObject.put("name", template.getName()); - jsonObject.put("minZoom", template.getMinimumZoomSupported()); - jsonObject.put("maxZoom", template.getMaximumZoomSupported()); - jsonObject.put("url", template.getUrlTemplate()); - jsonObject.put("randoms", template.getRandoms()); - jsonObject.put("ellipsoid", template.isEllipticYTile()); - jsonObject.put("inverted_y", template.isInvertedYTile()); - jsonObject.put("referer", template.getReferer()); - jsonObject.put("userAgent", template.getUserAgent()); - jsonObject.put("timesupported", template.isTimeSupported()); - jsonObject.put("expire", template.getExpirationTimeMinutes()); - jsonObject.put("inversiveZoom", template.getInversiveZoom()); - jsonObject.put("ext", template.getTileFormat()); - jsonObject.put("tileSize", template.getTileSize()); - jsonObject.put("bitDensity", template.getBitDensity()); - jsonObject.put("avgSize", template.getAvgSize()); - jsonObject.put("rule", template.getRule()); - jsonArray.put(jsonObject); - } - json.put("items", jsonArray); - - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); - LOG.error("Failed write to json", e); - } - } - } - - @Nullable - @Override - SettingsItemReader getReader() { - return getJsonReader(); - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return null; - } - } - - public static class AvoidRoadsSettingsItem extends CollectionSettingsItem { - - private OsmandSettings settings; - private AvoidSpecificRoads specificRoads; - - public AvoidRoadsSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { - super(app, null, items); - } - - public AvoidRoadsSettingsItem(@NonNull OsmandApplication app, @Nullable AvoidRoadsSettingsItem baseItem, @NonNull List items) { - super(app, baseItem, items); - } - - AvoidRoadsSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { - super(app, json); - } - - @Override - protected void init() { - super.init(); - settings = app.getSettings(); - specificRoads = app.getAvoidSpecificRoads(); - existingItems = new ArrayList<>(specificRoads.getImpassableRoads().values()); - } - - @NonNull - @Override - public SettingsItemType getType() { - return SettingsItemType.AVOID_ROADS; - } - - @NonNull - @Override - public String getName() { - return "avoid_roads"; - } - - @NonNull - @Override - public String getPublicName(@NonNull Context ctx) { - return "avoid_roads"; - } - - @Override - public void apply() { - List newItems = getNewItems(); - if (!newItems.isEmpty() || !duplicateItems.isEmpty()) { - appliedItems = new ArrayList<>(newItems); - for (AvoidRoadInfo duplicate : duplicateItems) { - if (shouldReplace) { - LatLon latLon = new LatLon(duplicate.latitude, duplicate.longitude); - if (settings.removeImpassableRoad(latLon)) { - settings.addImpassableRoad(duplicate); - } - } else { - settings.addImpassableRoad(renameItem(duplicate)); - } - } - for (AvoidRoadInfo avoidRoad : appliedItems) { - settings.addImpassableRoad(avoidRoad); - } - specificRoads.loadImpassableRoads(); - specificRoads.initRouteObjects(true); - } - } - - @Override - public boolean isDuplicate(@NonNull AvoidRoadInfo item) { - return existingItems.contains(item); - } - - @Override - public boolean shouldReadOnCollecting() { - return true; - } - - @NonNull - @Override - public AvoidRoadInfo renameItem(@NonNull AvoidRoadInfo item) { - int number = 0; - while (true) { - number++; - AvoidRoadInfo renamedItem = new AvoidRoadInfo(); - renamedItem.name = item.name + "_" + number; - if (!isDuplicate(renamedItem)) { - renamedItem.id = item.id; - renamedItem.latitude = item.latitude; - renamedItem.longitude = item.longitude; - renamedItem.appModeKey = item.appModeKey; - return renamedItem; - } - } - } - - @Override - void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { - try { - if (!json.has("items")) { - return; - } - JSONArray jsonArray = json.getJSONArray("items"); - for (int i = 0; i < jsonArray.length(); i++) { - JSONObject object = jsonArray.getJSONObject(i); - double latitude = object.optDouble("latitude"); - double longitude = object.optDouble("longitude"); - String name = object.optString("name"); - String appModeKey = object.optString("appModeKey"); - AvoidRoadInfo roadInfo = new AvoidRoadInfo(); - roadInfo.id = 0; - roadInfo.latitude = latitude; - roadInfo.longitude = longitude; - roadInfo.name = name; - if (ApplicationMode.valueOfStringKey(appModeKey, null) != null) { - roadInfo.appModeKey = appModeKey; - } else { - roadInfo.appModeKey = app.getRoutingHelper().getAppMode().getStringKey(); - } - items.add(roadInfo); - } - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); - throw new IllegalArgumentException("Json parse error", e); - } - } - - @Override - void writeItemsToJson(@NonNull JSONObject json) { - JSONArray jsonArray = new JSONArray(); - if (!items.isEmpty()) { - try { - for (AvoidRoadInfo avoidRoad : items) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("latitude", avoidRoad.latitude); - jsonObject.put("longitude", avoidRoad.longitude); - jsonObject.put("name", avoidRoad.name); - jsonObject.put("appModeKey", avoidRoad.appModeKey); - jsonArray.put(jsonObject); - } - json.put("items", jsonArray); - } catch (JSONException e) { - warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); - LOG.error("Failed write to json", e); - } - } - } - - @Nullable - @Override - SettingsItemReader getReader() { - return getJsonReader(); - } - - @Nullable - @Override - SettingsItemWriter getWriter() { - return null; - } - } - - private static class SettingsItemsFactory { - - private OsmandApplication app; - private List items = new ArrayList<>(); - - SettingsItemsFactory(@NonNull OsmandApplication app, String jsonStr) throws IllegalArgumentException, JSONException { - this.app = app; - collectItems(new JSONObject(jsonStr)); - } - - private void collectItems(JSONObject json) throws IllegalArgumentException, JSONException { - JSONArray itemsJson = json.getJSONArray("items"); - int version = json.has("version") ? json.getInt("version") : 1; - if (version > VERSION) { - throw new IllegalArgumentException("Unsupported osf version: " + version); - } - Map> pluginItems = new HashMap<>(); - for (int i = 0; i < itemsJson.length(); i++) { - JSONObject itemJson = itemsJson.getJSONObject(i); - SettingsItem item; - try { - item = createItem(itemJson); - items.add(item); - String pluginId = item.getPluginId(); - if (pluginId != null && item.getType() != SettingsItemType.PLUGIN) { - List items = pluginItems.get(pluginId); - if (items != null) { - items.add(item); - } else { - items = new ArrayList<>(); - items.add(item); - pluginItems.put(pluginId, items); - } - } - } catch (IllegalArgumentException e) { - LOG.error("Error creating item from json: " + itemJson, e); - } - } - if (items.size() == 0) { - throw new IllegalArgumentException("No items"); - } - for (SettingsItem item : items) { - if (item instanceof PluginSettingsItem) { - PluginSettingsItem pluginSettingsItem = ((PluginSettingsItem) item); - List pluginDependentItems = pluginItems.get(pluginSettingsItem.getName()); - if (!Algorithms.isEmpty(pluginDependentItems)) { - pluginSettingsItem.getPluginDependentItems().addAll(pluginDependentItems); - } - } - } - } - - @NonNull - public List getItems() { - return items; - } - - @Nullable - public SettingsItem getItemByFileName(@NonNull String fileName) { - for (SettingsItem item : items) { - if (Algorithms.stringsEqual(item.getFileName(), fileName)) { - return item; - } - } - return null; - } - - @NonNull - private SettingsItem createItem(@NonNull JSONObject json) throws IllegalArgumentException, JSONException { - SettingsItem item = null; - SettingsItemType type = SettingsItem.parseItemType(json); - OsmandSettings settings = app.getSettings(); - switch (type) { - case GLOBAL: - item = new GlobalSettingsItem(settings); - break; - case PROFILE: - item = new ProfileSettingsItem(app, json); - break; - case PLUGIN: - item = new PluginSettingsItem(app, json); - break; - case DATA: - item = new DataSettingsItem(app, json); - break; - case FILE: - item = new FileSettingsItem(app, json); - break; - case RESOURCES: - item = new ResourcesSettingsItem(app, json); - break; - case QUICK_ACTIONS: - item = new QuickActionsSettingsItem(app, json); - break; - case POI_UI_FILTERS: - item = new PoiUiFiltersSettingsItem(app, json); - break; - case MAP_SOURCES: - item = new MapSourcesSettingsItem(app, json); - break; - case AVOID_ROADS: - item = new AvoidRoadsSettingsItem(app, json); - break; - case SUGGESTED_DOWNLOADS: - item = new SuggestedDownloadsItem(app, json); - break; - case DOWNLOADS: - item = new DownloadsItem(app, json); - break; - } - return item; - } - } - - private static class SettingsExporter { - - private Map items; - private Map additionalParams; - private boolean exportItemsFiles; - - SettingsExporter(boolean exportItemsFiles) { - this.exportItemsFiles = exportItemsFiles; - items = new LinkedHashMap<>(); - additionalParams = new LinkedHashMap<>(); - } - - void addSettingsItem(SettingsItem item) throws IllegalArgumentException { - if (items.containsKey(item.getName())) { - throw new IllegalArgumentException("Already has such item: " + item.getName()); - } - items.put(item.getName(), item); - } - - void addAdditionalParam(String key, String value) { - additionalParams.put(key, value); - } - - void exportSettings(File file) throws JSONException, IOException { - JSONObject json = createItemsJson(); - OutputStream os = new BufferedOutputStream(new FileOutputStream(file), BUFFER); - ZipOutputStream zos = new ZipOutputStream(os); - try { - ZipEntry entry = new ZipEntry("items.json"); - zos.putNextEntry(entry); - zos.write(json.toString(2).getBytes("UTF-8")); - zos.closeEntry(); - if (exportItemsFiles) { - writeItemFiles(zos); - } - zos.flush(); - zos.finish(); - } finally { - Algorithms.closeStream(zos); - Algorithms.closeStream(os); - } - } - - private void writeItemFiles(ZipOutputStream zos) throws IOException { - for (SettingsItem item : items.values()) { - SettingsItemWriter writer = item.getWriter(); - if (writer != null) { - String fileName = item.getFileName(); - if (Algorithms.isEmpty(fileName)) { - fileName = item.getDefaultFileName(); - } - ZipEntry entry = new ZipEntry(fileName); - zos.putNextEntry(entry); - writer.writeToStream(zos); - zos.closeEntry(); - } - } - } - - private JSONObject createItemsJson() throws JSONException { - JSONObject json = new JSONObject(); - json.put("version", VERSION); - for (Map.Entry param : additionalParams.entrySet()) { - json.put(param.getKey(), param.getValue()); - } - JSONArray itemsJson = new JSONArray(); - for (SettingsItem item : items.values()) { - itemsJson.put(new JSONObject(item.toJson())); - } - json.put("items", itemsJson); - return json; - } - } - - private static class SettingsImporter { - - private OsmandApplication app; - - SettingsImporter(@NonNull OsmandApplication app) { - this.app = app; - } - - List collectItems(@NonNull File file) throws IllegalArgumentException, IOException { - return processItems(file, null); - } - - void importItems(@NonNull File file, @NonNull List items) throws IllegalArgumentException, IOException { - processItems(file, items); - } - - private List getItemsFromJson(@NonNull File file) throws IOException { - List items = new ArrayList<>(); - ZipInputStream zis = new ZipInputStream(new FileInputStream(file)); - InputStream ois = new BufferedInputStream(zis); - try { - ZipEntry entry; - while ((entry = zis.getNextEntry()) != null) { - String fileName = checkEntryName(entry.getName()); - if (fileName.equals("items.json")) { - String itemsJson = null; - try { - itemsJson = Algorithms.readFromInputStream(ois, false).toString(); - } catch (IOException e) { - LOG.error("Error reading items.json: " + itemsJson, e); - throw new IllegalArgumentException("No items"); - } finally { - zis.closeEntry(); - } - try { - SettingsItemsFactory itemsFactory = new SettingsItemsFactory(app, itemsJson); - items.addAll(itemsFactory.getItems()); - } catch (IllegalArgumentException e) { - LOG.error("Error parsing items: " + itemsJson, e); - throw new IllegalArgumentException("No items"); - } catch (JSONException e) { - LOG.error("Error parsing items: " + itemsJson, e); - throw new IllegalArgumentException("No items"); - } - break; - } - } - } catch (IOException ex) { - LOG.error("Failed to read next entry", ex); - } finally { - Algorithms.closeStream(ois); - Algorithms.closeStream(zis); - } - return items; - } - - private List processItems(@NonNull File file, @Nullable List items) throws IllegalArgumentException, IOException { - boolean collecting = items == null; - if (collecting) { - items = getItemsFromJson(file); - } else { - if (items.size() == 0) { - throw new IllegalArgumentException("No items"); - } - } - ZipInputStream zis = new ZipInputStream(new FileInputStream(file)); - InputStream ois = new BufferedInputStream(zis); - try { - ZipEntry entry; - while ((entry = zis.getNextEntry()) != null) { - String fileName = checkEntryName(entry.getName()); - SettingsItem item = null; - for (SettingsItem settingsItem : items) { - if (settingsItem != null && settingsItem.applyFileName(fileName)) { - item = settingsItem; - break; - } - } - if (item != null && collecting && item.shouldReadOnCollecting() - || item != null && !collecting && !item.shouldReadOnCollecting()) { - try { - SettingsItemReader reader = item.getReader(); - if (reader != null) { - reader.readFromStream(ois); - } - } catch (IllegalArgumentException e) { - item.warnings.add(app.getString(R.string.settings_item_read_error, item.getName())); - LOG.error("Error reading item data: " + item.getName(), e); - } catch (IOException e) { - item.warnings.add(app.getString(R.string.settings_item_read_error, item.getName())); - LOG.error("Error reading item data: " + item.getName(), e); - } finally { - zis.closeEntry(); - } - } - } - } catch (IOException ex) { - LOG.error("Failed to read next entry", ex); - } finally { - Algorithms.closeStream(ois); - Algorithms.closeStream(zis); - } - return items; - } - - private String checkEntryName(String entryName) { - String fileExt = OSMAND_SETTINGS_FILE_EXT + "/"; - int index = entryName.indexOf(fileExt); - if (index != -1) { - entryName = entryName.substring(index + fileExt.length()); - } - return entryName; - } - } - - public static Map> getSettingsToOperate(List settingsItems, boolean importComplete) { - Map> settingsToOperate = new HashMap<>(); - List profiles = new ArrayList<>(); - List quickActions = new ArrayList<>(); - List poiUIFilters = new ArrayList<>(); - List tileSourceTemplates = new ArrayList<>(); - List routingFilesList = new ArrayList<>(); - List renderFilesList = new ArrayList<>(); - List avoidRoads = new ArrayList<>(); - for (SettingsItem item : settingsItems) { - switch (item.getType()) { - case PROFILE: - profiles.add(((ProfileSettingsItem) item).getModeBean()); - break; - case FILE: - FileSettingsItem fileItem = (FileSettingsItem) item; - if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.RENDERING_STYLE) { - renderFilesList.add(fileItem.getFile()); - } else if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.ROUTING_CONFIG) { - routingFilesList.add(fileItem.getFile()); - } - break; - case QUICK_ACTIONS: - QuickActionsSettingsItem quickActionsItem = (QuickActionsSettingsItem) item; - if (importComplete) { - quickActions.addAll(quickActionsItem.getAppliedItems()); - } else { - quickActions.addAll(quickActionsItem.getItems()); - } - break; - case POI_UI_FILTERS: - PoiUiFiltersSettingsItem poiUiFilterItem = (PoiUiFiltersSettingsItem) item; - if (importComplete) { - poiUIFilters.addAll(poiUiFilterItem.getAppliedItems()); - } else { - poiUIFilters.addAll(poiUiFilterItem.getItems()); - } - break; - case MAP_SOURCES: - MapSourcesSettingsItem mapSourcesItem = (MapSourcesSettingsItem) item; - if (importComplete) { - tileSourceTemplates.addAll(mapSourcesItem.getAppliedItems()); - } else { - tileSourceTemplates.addAll(mapSourcesItem.getItems()); - } - break; - case AVOID_ROADS: - AvoidRoadsSettingsItem avoidRoadsItem = (AvoidRoadsSettingsItem) item; - if (importComplete) { - avoidRoads.addAll(avoidRoadsItem.getAppliedItems()); - } else { - avoidRoads.addAll(avoidRoadsItem.getItems()); - } - break; - default: - break; - } - } - - if (!profiles.isEmpty()) { - settingsToOperate.put(ExportSettingsType.PROFILE, profiles); - } - if (!quickActions.isEmpty()) { - settingsToOperate.put(ExportSettingsType.QUICK_ACTIONS, quickActions); - } - if (!poiUIFilters.isEmpty()) { - settingsToOperate.put(ExportSettingsType.POI_TYPES, poiUIFilters); - } - if (!tileSourceTemplates.isEmpty()) { - settingsToOperate.put(ExportSettingsType.MAP_SOURCES, tileSourceTemplates); - } - if (!renderFilesList.isEmpty()) { - settingsToOperate.put(ExportSettingsType.CUSTOM_RENDER_STYLE, renderFilesList); - } - if (!routingFilesList.isEmpty()) { - settingsToOperate.put(ExportSettingsType.CUSTOM_ROUTING, routingFilesList); - } - if (!avoidRoads.isEmpty()) { - settingsToOperate.put(ExportSettingsType.AVOID_ROADS, avoidRoads); - } - return settingsToOperate; - } - - @SuppressLint("StaticFieldLeak") - public class ImportAsyncTask extends AsyncTask> { - - private File file; - private String latestChanges; - private int version; - - private SettingsImportListener importListener; - private SettingsCollectListener collectListener; - private CheckDuplicatesListener duplicatesListener; - private SettingsImporter importer; - - private List items = new ArrayList<>(); - private List selectedItems = new ArrayList<>(); - private List duplicates; - - private ImportType importType; - private boolean importDone; - - ImportAsyncTask(@NonNull File file, String latestChanges, int version, @Nullable SettingsCollectListener collectListener) { - this.file = file; - this.collectListener = collectListener; - this.latestChanges = latestChanges; - this.version = version; - importer = new SettingsImporter(app); - importType = ImportType.COLLECT; - } - - ImportAsyncTask(@NonNull File file, @NonNull List items, String latestChanges, int version, @Nullable SettingsImportListener importListener) { - this.file = file; - this.importListener = importListener; - this.items = items; - this.latestChanges = latestChanges; - this.version = version; - importer = new SettingsImporter(app); - importType = ImportType.IMPORT; - } - - ImportAsyncTask(@NonNull File file, @NonNull List items, @NonNull List selectedItems, @Nullable CheckDuplicatesListener duplicatesListener) { - this.file = file; - this.items = items; - this.duplicatesListener = duplicatesListener; - this.selectedItems = selectedItems; - importer = new SettingsImporter(app); - importType = ImportType.CHECK_DUPLICATES; - } - - @Override - protected void onPreExecute() { - ImportAsyncTask importTask = SettingsHelper.this.importTask; - if (importTask != null && !importTask.importDone) { - finishImport(importListener, false, items); - } - SettingsHelper.this.importTask = this; - } - - @Override - protected List doInBackground(Void... voids) { - switch (importType) { - case COLLECT: - try { - return importer.collectItems(file); - } catch (IllegalArgumentException e) { - LOG.error("Failed to collect items from: " + file.getName(), e); - } catch (IOException e) { - LOG.error("Failed to collect items from: " + file.getName(), e); - } - break; - case CHECK_DUPLICATES: - this.duplicates = getDuplicatesData(selectedItems); - return selectedItems; - case IMPORT: - return items; - } - return null; - } - - @Override - protected void onPostExecute(@Nullable List items) { - if (items != null && importType != ImportType.CHECK_DUPLICATES) { - this.items = items; - } else { - selectedItems = items; - } - switch (importType) { - case COLLECT: - importDone = true; - collectListener.onSettingsCollectFinished(true, false, this.items); - break; - case CHECK_DUPLICATES: - importDone = true; - if (duplicatesListener != null) { - duplicatesListener.onDuplicatesChecked(duplicates, selectedItems); - } - break; - case IMPORT: - if (items != null && items.size() > 0) { - for (SettingsItem item : items) { - item.apply(); - } - new ImportItemsAsyncTask(file, importListener, items).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - break; - } - } - - public List getItems() { - return items; - } - - public File getFile() { - return file; - } - - public void setImportListener(SettingsImportListener importListener) { - this.importListener = importListener; - } - - public void setDuplicatesListener(CheckDuplicatesListener duplicatesListener) { - this.duplicatesListener = duplicatesListener; - } - - ImportType getImportType() { - return importType; - } - - boolean isImportDone() { - return importDone; - } - - public List getDuplicates() { - return duplicates; - } - - public List getSelectedItems() { - return selectedItems; - } - - private List getDuplicatesData(List items) { - List duplicateItems = new ArrayList<>(); - for (SettingsItem item : items) { - if (item instanceof ProfileSettingsItem) { - if (item.exists()) { - duplicateItems.add(((ProfileSettingsItem) item).getModeBean()); - } - } else if (item instanceof CollectionSettingsItem) { - List duplicates = ((CollectionSettingsItem) item).processDuplicateItems(); - if (!duplicates.isEmpty()) { - duplicateItems.addAll(duplicates); - } - } else if (item instanceof FileSettingsItem) { - if (item.exists()) { - duplicateItems.add(((FileSettingsItem) item).getFile()); - } - } - } - return duplicateItems; - } - } - - @Nullable - public ImportAsyncTask getImportTask() { - return importTask; - } - - @Nullable - public ImportType getImportTaskType() { - ImportAsyncTask importTask = this.importTask; - return importTask != null ? importTask.getImportType() : null; - } - - public boolean isImportDone() { - ImportAsyncTask importTask = this.importTask; - return importTask == null || importTask.isImportDone(); - } - - public boolean isFileExporting(File file) { - return exportAsyncTasks.containsKey(file); - } - - public void updateExportListener(File file, SettingsExportListener listener) { - ExportAsyncTask exportAsyncTask = exportAsyncTasks.get(file); - if (exportAsyncTask != null) { - exportAsyncTask.listener = listener; - } - } - - @SuppressLint("StaticFieldLeak") - private class ImportItemsAsyncTask extends AsyncTask { - - private SettingsImporter importer; - private File file; - private SettingsImportListener listener; - private List items; - - ImportItemsAsyncTask(@NonNull File file, - @Nullable SettingsImportListener listener, - @NonNull List items) { - importer = new SettingsImporter(app); - this.file = file; - this.listener = listener; - this.items = items; - } - - @Override - protected Boolean doInBackground(Void... voids) { - try { - importer.importItems(file, items); - return true; - } catch (IllegalArgumentException e) { - LOG.error("Failed to import items from: " + file.getName(), e); - } catch (IOException e) { - LOG.error("Failed to import items from: " + file.getName(), e); - } - return false; - } - - @Override - protected void onPostExecute(Boolean success) { - finishImport(listener, success, items); - } - } - - private void finishImport(@Nullable SettingsImportListener listener, boolean success, @NonNull List items) { - importTask = null; - List warnings = new ArrayList<>(); - for (SettingsItem item : items) { - warnings.addAll(item.getWarnings()); - } - if (!warnings.isEmpty()) { - app.showToastMessage(AndroidUtils.formatWarnings(warnings).toString()); - } - if (listener != null) { - listener.onSettingsImportFinished(success, items); - } - } - - @SuppressLint("StaticFieldLeak") - private class ExportAsyncTask extends AsyncTask { - - private SettingsExporter exporter; - private File file; - private SettingsExportListener listener; - - ExportAsyncTask(@NonNull File settingsFile, - @Nullable SettingsExportListener listener, - @NonNull List items, boolean exportItemsFiles) { - this.file = settingsFile; - this.listener = listener; - this.exporter = new SettingsExporter(exportItemsFiles); - for (SettingsItem item : items) { - exporter.addSettingsItem(item); - } - } - - @Override - protected Boolean doInBackground(Void... voids) { - try { - exporter.exportSettings(file); - return true; - } catch (JSONException e) { - LOG.error("Failed to export items to: " + file.getName(), e); - } catch (IOException e) { - LOG.error("Failed to export items to: " + file.getName(), e); - } - return false; - } - - @Override - protected void onPostExecute(Boolean success) { - exportAsyncTasks.remove(file); - if (listener != null) { - listener.onSettingsExportFinished(file, success); - } - } - } - - public void collectSettings(@NonNull File settingsFile, String latestChanges, int version, - @Nullable SettingsCollectListener listener) { - new ImportAsyncTask(settingsFile, latestChanges, version, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - public void checkDuplicates(@NonNull File file, @NonNull List items, @NonNull List selectedItems, CheckDuplicatesListener listener) { - new ImportAsyncTask(file, items, selectedItems, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - public void importSettings(@NonNull File settingsFile, @NonNull List items, String latestChanges, int version, @Nullable SettingsImportListener listener) { - new ImportAsyncTask(settingsFile, items, latestChanges, version, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - public void exportSettings(@NonNull File fileDir, @NonNull String fileName, @Nullable SettingsExportListener listener, @NonNull List items, boolean exportItemsFiles) { - File file = new File(fileDir, fileName + OSMAND_SETTINGS_FILE_EXT); - ExportAsyncTask exportAsyncTask = new ExportAsyncTask(file, listener, items, exportItemsFiles); - exportAsyncTasks.put(file, exportAsyncTask); - exportAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - - public void exportSettings(@NonNull File fileDir, @NonNull String fileName, @Nullable SettingsExportListener listener, - boolean exportItemsFiles, @NonNull SettingsItem... items) { - exportSettings(fileDir, fileName, listener, new ArrayList<>(Arrays.asList(items)), exportItemsFiles); - } - - public enum ImportType { - COLLECT, - CHECK_DUPLICATES, - IMPORT - } - - public List getFilteredSettingsItems(Map> additionalData, - List settingsTypes) { - List settingsItems = new ArrayList<>(); - for (ExportSettingsType settingsType : settingsTypes) { - List settingsDataObjects = additionalData.get(settingsType); - if (settingsDataObjects != null) { - for (Object object : settingsDataObjects) { - if (object instanceof ApplicationModeBean) { - settingsItems.add(new ProfileSettingsItem(app, null, (ApplicationModeBean) object)); - } - } - settingsItems.addAll(prepareAdditionalSettingsItems(new ArrayList<>(settingsDataObjects))); - } - } - return settingsItems; - } - - public Map> getAdditionalData() { - Map> dataList = new HashMap<>(); - - QuickActionRegistry registry = app.getQuickActionRegistry(); - List actionsList = registry.getQuickActions(); - if (!actionsList.isEmpty()) { - dataList.put(ExportSettingsType.QUICK_ACTIONS, actionsList); - } - - List poiList = app.getPoiFilters().getUserDefinedPoiFilters(false); - if (!poiList.isEmpty()) { - dataList.put(ExportSettingsType.POI_TYPES, poiList); - } - - List iTileSources = new ArrayList<>(); - Set tileSourceNames = app.getSettings().getTileSourceEntries(true).keySet(); - for (String name : tileSourceNames) { - File f = app.getAppPath(IndexConstants.TILES_INDEX_DIR + name); - if (f != null) { - ITileSource template; - if (f.getName().endsWith(SQLiteTileSource.EXT)) { - template = new SQLiteTileSource(app, f, TileSourceManager.getKnownSourceTemplates()); - } else { - template = TileSourceManager.createTileSourceTemplate(f); - } - if (template.getUrlTemplate() != null) { - iTileSources.add(template); - } - } - } - if (!iTileSources.isEmpty()) { - dataList.put(ExportSettingsType.MAP_SOURCES, iTileSources); - } - - Map externalRenderers = app.getRendererRegistry().getExternalRenderers(); - if (!externalRenderers.isEmpty()) { - dataList.put(ExportSettingsType.CUSTOM_RENDER_STYLE, new ArrayList<>(externalRenderers.values())); - } - - File routingProfilesFolder = app.getAppPath(IndexConstants.ROUTING_PROFILES_DIR); - if (routingProfilesFolder.exists() && routingProfilesFolder.isDirectory()) { - File[] fl = routingProfilesFolder.listFiles(); - if (fl != null && fl.length > 0) { - dataList.put(ExportSettingsType.CUSTOM_ROUTING, Arrays.asList(fl)); - } - } - - Map impassableRoads = app.getAvoidSpecificRoads().getImpassableRoads(); - if (!impassableRoads.isEmpty()) { - dataList.put(ExportSettingsType.AVOID_ROADS, new ArrayList<>(impassableRoads.values())); - } - return dataList; - } - - public List prepareAdditionalSettingsItems(List data) { - List settingsItems = new ArrayList<>(); - List quickActions = new ArrayList<>(); - List poiUIFilters = new ArrayList<>(); - List tileSourceTemplates = new ArrayList<>(); - List avoidRoads = new ArrayList<>(); - for (Object object : data) { - if (object instanceof QuickAction) { - quickActions.add((QuickAction) object); - } else if (object instanceof PoiUIFilter) { - poiUIFilters.add((PoiUIFilter) object); - } else if (object instanceof TileSourceTemplate || object instanceof SQLiteTileSource) { - tileSourceTemplates.add((ITileSource) object); - } else if (object instanceof File) { - try { - settingsItems.add(new FileSettingsItem(app, (File) object)); - } catch (IllegalArgumentException e) { - LOG.warn("Trying to export unsuported file type", e); - } - } else if (object instanceof AvoidRoadInfo) { - avoidRoads.add((AvoidRoadInfo) object); - } - } - if (!quickActions.isEmpty()) { - settingsItems.add(new QuickActionsSettingsItem(app, quickActions)); - } - if (!poiUIFilters.isEmpty()) { - settingsItems.add(new PoiUiFiltersSettingsItem(app, poiUIFilters)); - } - if (!tileSourceTemplates.isEmpty()) { - settingsItems.add(new MapSourcesSettingsItem(app, tileSourceTemplates)); - } - if (!avoidRoads.isEmpty()) { - settingsItems.add(new AvoidRoadsSettingsItem(app, avoidRoads)); - } - return settingsItems; - } -} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/AvoidRoadsSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/AvoidRoadsSettingsItem.java new file mode 100644 index 0000000000..3088d9e86f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/AvoidRoadsSettingsItem.java @@ -0,0 +1,179 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.data.LatLon; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.helpers.AvoidSpecificRoads; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.OsmandSettings; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class AvoidRoadsSettingsItem extends CollectionSettingsItem { + + private OsmandSettings settings; + private AvoidSpecificRoads specificRoads; + + public AvoidRoadsSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { + super(app, null, items); + } + + public AvoidRoadsSettingsItem(@NonNull OsmandApplication app, @Nullable AvoidRoadsSettingsItem baseItem, @NonNull List items) { + super(app, baseItem, items); + } + + AvoidRoadsSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + @Override + protected void init() { + super.init(); + settings = app.getSettings(); + specificRoads = app.getAvoidSpecificRoads(); + existingItems = new ArrayList<>(specificRoads.getImpassableRoads().values()); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.AVOID_ROADS; + } + + @NonNull + @Override + public String getName() { + return "avoid_roads"; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return "avoid_roads"; + } + + @Override + public void apply() { + List newItems = getNewItems(); + if (!newItems.isEmpty() || !duplicateItems.isEmpty()) { + appliedItems = new ArrayList<>(newItems); + for (AvoidSpecificRoads.AvoidRoadInfo duplicate : duplicateItems) { + if (shouldReplace) { + LatLon latLon = new LatLon(duplicate.latitude, duplicate.longitude); + if (settings.removeImpassableRoad(latLon)) { + settings.addImpassableRoad(duplicate); + } + } else { + settings.addImpassableRoad(renameItem(duplicate)); + } + } + for (AvoidSpecificRoads.AvoidRoadInfo avoidRoad : appliedItems) { + settings.addImpassableRoad(avoidRoad); + } + specificRoads.loadImpassableRoads(); + specificRoads.initRouteObjects(true); + } + } + + @Override + public boolean isDuplicate(@NonNull AvoidSpecificRoads.AvoidRoadInfo item) { + return existingItems.contains(item); + } + + @Override + public boolean shouldReadOnCollecting() { + return true; + } + + @NonNull + @Override + public AvoidSpecificRoads.AvoidRoadInfo renameItem(@NonNull AvoidSpecificRoads.AvoidRoadInfo item) { + int number = 0; + while (true) { + number++; + AvoidSpecificRoads.AvoidRoadInfo renamedItem = new AvoidSpecificRoads.AvoidRoadInfo(); + renamedItem.name = item.name + "_" + number; + if (!isDuplicate(renamedItem)) { + renamedItem.id = item.id; + renamedItem.latitude = item.latitude; + renamedItem.longitude = item.longitude; + renamedItem.appModeKey = item.appModeKey; + return renamedItem; + } + } + } + + @Override + void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { + try { + if (!json.has("items")) { + return; + } + JSONArray jsonArray = json.getJSONArray("items"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject object = jsonArray.getJSONObject(i); + double latitude = object.optDouble("latitude"); + double longitude = object.optDouble("longitude"); + String name = object.optString("name"); + String appModeKey = object.optString("appModeKey"); + AvoidSpecificRoads.AvoidRoadInfo roadInfo = new AvoidSpecificRoads.AvoidRoadInfo(); + roadInfo.id = 0; + roadInfo.latitude = latitude; + roadInfo.longitude = longitude; + roadInfo.name = name; + if (ApplicationMode.valueOfStringKey(appModeKey, null) != null) { + roadInfo.appModeKey = appModeKey; + } else { + roadInfo.appModeKey = app.getRoutingHelper().getAppMode().getStringKey(); + } + items.add(roadInfo); + } + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); + throw new IllegalArgumentException("Json parse error", e); + } + } + + @Override + void writeItemsToJson(@NonNull JSONObject json) { + JSONArray jsonArray = new JSONArray(); + if (!items.isEmpty()) { + try { + for (AvoidSpecificRoads.AvoidRoadInfo avoidRoad : items) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("latitude", avoidRoad.latitude); + jsonObject.put("longitude", avoidRoad.longitude); + jsonObject.put("name", avoidRoad.name); + jsonObject.put("appModeKey", avoidRoad.appModeKey); + jsonArray.put(jsonObject); + } + json.put("items", jsonArray); + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); + SettingsHelper.LOG.error("Failed write to json", e); + } + } + } + + @Nullable + @Override + SettingsItemReader getReader() { + return getJsonReader(); + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/CollectionSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/CollectionSettingsItem.java new file mode 100644 index 0000000000..dea9d51bc8 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/CollectionSettingsItem.java @@ -0,0 +1,75 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public abstract class CollectionSettingsItem extends SettingsItem { + + protected List items; + protected List appliedItems; + protected List duplicateItems; + protected List existingItems; + + @Override + protected void init() { + super.init(); + items = new ArrayList<>(); + appliedItems = new ArrayList<>(); + duplicateItems = new ArrayList<>(); + } + + CollectionSettingsItem(@NonNull OsmandApplication app, @Nullable CollectionSettingsItem baseItem, @NonNull List items) { + super(app, baseItem); + this.items = items; + } + + CollectionSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + @NonNull + public List getItems() { + return items; + } + + @NonNull + public List getAppliedItems() { + return appliedItems; + } + + @NonNull + public List getDuplicateItems() { + return duplicateItems; + } + + @NonNull + public List processDuplicateItems() { + if (!items.isEmpty()) { + for (T item : items) { + if (isDuplicate(item)) { + duplicateItems.add(item); + } + } + } + return duplicateItems; + } + + public List getNewItems() { + List res = new ArrayList<>(items); + res.removeAll(duplicateItems); + return res; + } + + public abstract boolean isDuplicate(@NonNull T item); + + @NonNull + public abstract T renameItem(@NonNull T item); +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/DataSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/DataSettingsItem.java new file mode 100644 index 0000000000..d68325fe5f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/DataSettingsItem.java @@ -0,0 +1,87 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; +import net.osmand.util.Algorithms; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +public class DataSettingsItem extends StreamSettingsItem { + + @Nullable + private byte[] data; + + public DataSettingsItem(@NonNull OsmandApplication app, @NonNull String name) { + super(app, name); + } + + DataSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + public DataSettingsItem(@NonNull OsmandApplication app, @NonNull byte[] data, @NonNull String name) { + super(app, name); + this.data = data; + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.DATA; + } + + @NonNull + @Override + public String getDefaultFileExtension() { + return ".dat"; + } + + @Nullable + public byte[] getData() { + return data; + } + + @Override + void readFromJson(@NonNull JSONObject json) throws JSONException { + super.readFromJson(json); + String fileName = getFileName(); + if (!Algorithms.isEmpty(fileName)) { + name = Algorithms.getFileNameWithoutExtension(new File(fileName)); + } + } + + @Nullable + @Override + SettingsItemReader getReader() { + return new StreamSettingsItemReader(this) { + @Override + public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[SettingsHelper.BUFFER]; + while ((nRead = inputStream.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + + buffer.flush(); + DataSettingsItem.this.data = buffer.toByteArray(); + } + }; + } + + @Nullable + @Override + public SettingsItemWriter getWriter() { + setInputStream(new ByteArrayInputStream(data)); + return super.getWriter(); + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/DownloadsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/DownloadsItem.java new file mode 100644 index 0000000000..67b9d5812e --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/DownloadsItem.java @@ -0,0 +1,102 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.map.WorldRegion; +import net.osmand.plus.CustomOsmandPlugin; +import net.osmand.plus.CustomRegion; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class DownloadsItem extends SettingsItem { + + private List items; + + DownloadsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + @Override + protected void init() { + super.init(); + items = new ArrayList<>(); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.DOWNLOADS; + + } + + @NonNull + @Override + public String getName() { + return "downloads"; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return "downloads"; + } + + public List getItems() { + return items; + } + + @Override + void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { + try { + if (!json.has("items")) { + return; + } + JSONArray jsonArray = json.getJSONArray("items"); + items.addAll(CustomOsmandPlugin.collectRegionsFromJson(app, jsonArray)); + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); + throw new IllegalArgumentException("Json parse error", e); + } + } + + @Override + void writeItemsToJson(@NonNull JSONObject json) { + JSONArray jsonArray = new JSONArray(); + if (!items.isEmpty()) { + try { + for (WorldRegion region : items) { + if (region instanceof CustomRegion) { + JSONObject regionJson = ((CustomRegion) region).toJson(); + jsonArray.put(regionJson); + } + } + json.put("items", jsonArray); + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); + SettingsHelper.LOG.error("Failed write to json", e); + } + } + } + + @Nullable + @Override + SettingsItemReader getReader() { + return null; + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java new file mode 100644 index 0000000000..357c0aadbf --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java @@ -0,0 +1,242 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.IndexConstants; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.util.Algorithms; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class FileSettingsItem extends StreamSettingsItem { + + public enum FileSubtype { + UNKNOWN("", null), + OTHER("other", ""), + ROUTING_CONFIG("routing_config", IndexConstants.ROUTING_PROFILES_DIR), + RENDERING_STYLE("rendering_style", IndexConstants.RENDERERS_DIR), + OBF_MAP("obf_map", IndexConstants.MAPS_PATH), + TILES_MAP("tiles_map", IndexConstants.TILES_INDEX_DIR), + GPX("gpx", IndexConstants.GPX_INDEX_DIR), + VOICE("voice", IndexConstants.VOICE_INDEX_DIR), + TRAVEL("travel", IndexConstants.WIKIVOYAGE_INDEX_DIR), + MULTIMEDIA_NOTES("multimedia_notes", IndexConstants.AV_INDEX_DIR); + + private String subtypeName; + private String subtypeFolder; + + FileSubtype(String subtypeName, String subtypeFolder) { + this.subtypeName = subtypeName; + this.subtypeFolder = subtypeFolder; + } + + public String getSubtypeName() { + return subtypeName; + } + + public String getSubtypeFolder() { + return subtypeFolder; + } + + public static FileSubtype getSubtypeByName(@NonNull String name) { + for (FileSubtype subtype : FileSubtype.values()) { + if (name.equals(subtype.subtypeName)) { + return subtype; + } + } + return null; + } + + public static FileSubtype getSubtypeByFileName(@NonNull String fileName) { + String name = fileName; + if (fileName.startsWith(File.separator)) { + name = fileName.substring(1); + } + for (FileSubtype subtype : FileSubtype.values()) { + switch (subtype) { + case UNKNOWN: + case OTHER: + break; + case OBF_MAP: + if (name.endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { + return subtype; + } + break; + default: + if (name.startsWith(subtype.subtypeFolder)) { + return subtype; + } + break; + } + } + return UNKNOWN; + } + + @NonNull + @Override + public String toString() { + return subtypeName; + } + } + + protected File file; + private File appPath; + protected FileSubtype subtype; + + public FileSettingsItem(@NonNull OsmandApplication app, @NonNull File file) throws IllegalArgumentException { + super(app, file.getPath().replace(app.getAppPath(null).getPath(), "")); + this.file = file; + this.appPath = app.getAppPath(null); + String fileName = getFileName(); + if (fileName != null) { + this.subtype = FileSubtype.getSubtypeByFileName(fileName); + } + if (subtype == FileSubtype.UNKNOWN || subtype == null) { + throw new IllegalArgumentException("Unknown file subtype: " + fileName); + } + } + + FileSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + this.appPath = app.getAppPath(null); + if (subtype == FileSubtype.OTHER) { + this.file = new File(appPath, name); + } else if (subtype == FileSubtype.UNKNOWN || subtype == null) { + throw new IllegalArgumentException("Unknown file subtype: " + getFileName()); + } else { + String subtypeFolder = subtype.subtypeFolder; + int nameIndex = fileName.indexOf(name); + int folderIndex = fileName.indexOf(subtype.subtypeFolder); + if (nameIndex != -1 && folderIndex != -1) { + String subfolderPath = fileName.substring(folderIndex + subtype.subtypeFolder.length(), nameIndex); + subtypeFolder = subtypeFolder + subfolderPath; + } + this.file = new File(app.getAppPath(subtypeFolder), name); + } + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.FILE; + } + + public File getPluginPath() { + String pluginId = getPluginId(); + if (!Algorithms.isEmpty(pluginId)) { + return new File(appPath, IndexConstants.PLUGINS_DIR + pluginId); + } + return appPath; + } + + @Override + void readFromJson(@NonNull JSONObject json) throws JSONException { + super.readFromJson(json); + String fileName = getFileName(); + if (subtype == null) { + String subtypeStr = json.has("subtype") ? json.getString("subtype") : null; + if (!Algorithms.isEmpty(subtypeStr)) { + subtype = FileSubtype.getSubtypeByName(subtypeStr); + } else if (!Algorithms.isEmpty(fileName)) { + subtype = FileSubtype.getSubtypeByFileName(fileName); + } else { + subtype = FileSubtype.UNKNOWN; + } + } + if (!Algorithms.isEmpty(fileName)) { + if (subtype == FileSubtype.OTHER) { + name = fileName; + } else if (subtype != null && subtype != FileSubtype.UNKNOWN) { + name = Algorithms.getFileWithoutDirs(fileName); + } + } + } + + @Override + void writeToJson(@NonNull JSONObject json) throws JSONException { + super.writeToJson(json); + if (subtype != null) { + json.put("subtype", subtype.getSubtypeName()); + } + } + + @NonNull + public File getFile() { + return file; + } + + @NonNull + public FileSubtype getSubtype() { + return subtype; + } + + @Override + public boolean exists() { + return file.exists(); + } + + private File renameFile(File file) { + int number = 0; + String path = file.getAbsolutePath(); + while (true) { + number++; + String copyName = path.replaceAll(file.getName(), file.getName().replaceFirst("[.]", "_" + number + ".")); + File copyFile = new File(copyName); + if (!copyFile.exists()) { + return copyFile; + } + } + } + + @Nullable + @Override + SettingsItemReader getReader() { + return new StreamSettingsItemReader(this) { + @Override + public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { + OutputStream output; + File dest = FileSettingsItem.this.file; + if (dest.exists() && !shouldReplace) { + dest = renameFile(dest); + } + if (dest.getParentFile() != null && !dest.getParentFile().exists()) { + dest.getParentFile().mkdirs(); + } + output = new FileOutputStream(dest); + byte[] buffer = new byte[SettingsHelper.BUFFER]; + int count; + try { + while ((count = inputStream.read(buffer)) != -1) { + output.write(buffer, 0, count); + } + output.flush(); + } finally { + Algorithms.closeStream(output); + } + } + }; + } + + @Nullable + @Override + public SettingsItemWriter getWriter() { + try { + setInputStream(new FileInputStream(file)); + } catch (FileNotFoundException e) { + warnings.add(app.getString(R.string.settings_item_read_error, file.getName())); + SettingsHelper.LOG.error("Failed to set input stream from file: " + file.getName(), e); + } + return super.getWriter(); + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/GlobalSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/GlobalSettingsItem.java new file mode 100644 index 0000000000..ca7de253fb --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/GlobalSettingsItem.java @@ -0,0 +1,65 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.R; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; + +import org.json.JSONException; +import org.json.JSONObject; + +public class GlobalSettingsItem extends OsmandSettingsItem { + + public GlobalSettingsItem(@NonNull OsmandSettings settings) { + super(settings); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.GLOBAL; + } + + @NonNull + @Override + public String getName() { + return "general_settings"; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return ctx.getString(R.string.general_settings_2); + } + + @Override + public boolean exists() { + return true; + } + + @Nullable + @Override + SettingsItemReader getReader() { + return new OsmandSettingsItemReader(this, getSettings()) { + @Override + protected void readPreferenceFromJson(@NonNull OsmandPreference preference, @NonNull JSONObject json) throws JSONException { + preference.readFromJson(json, null); + } + }; + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return new OsmandSettingsItemWriter(this, getSettings()) { + @Override + protected void writePreferenceToJson(@NonNull OsmandPreference preference, @NonNull JSONObject json) throws JSONException { + preference.writeToJson(json, null); + } + }; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/MapSourcesSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/MapSourcesSettingsItem.java new file mode 100644 index 0000000000..82cf840b94 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/MapSourcesSettingsItem.java @@ -0,0 +1,241 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.IndexConstants; +import net.osmand.map.ITileSource; +import net.osmand.map.TileSourceManager; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.SQLiteTileSource; +import net.osmand.util.Algorithms; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +public class MapSourcesSettingsItem extends CollectionSettingsItem { + + private List existingItemsNames; + + public MapSourcesSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { + super(app, null, items); + } + + public MapSourcesSettingsItem(@NonNull OsmandApplication app, @Nullable MapSourcesSettingsItem baseItem, @NonNull List items) { + super(app, baseItem, items); + } + + MapSourcesSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + @Override + protected void init() { + super.init(); + existingItemsNames = new ArrayList<>(app.getSettings().getTileSourceEntries().values()); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.MAP_SOURCES; + } + + @Override + public void apply() { + List newItems = getNewItems(); + if (!newItems.isEmpty() || !duplicateItems.isEmpty()) { + appliedItems = new ArrayList<>(newItems); + if (shouldReplace) { + for (ITileSource tileSource : duplicateItems) { + if (tileSource instanceof SQLiteTileSource) { + File f = app.getAppPath(IndexConstants.TILES_INDEX_DIR + tileSource.getName() + IndexConstants.SQLITE_EXT); + if (f != null && f.exists() && Algorithms.removeAllFiles(f)) { + appliedItems.add(tileSource); + } + } else if (tileSource instanceof TileSourceManager.TileSourceTemplate) { + File f = app.getAppPath(IndexConstants.TILES_INDEX_DIR + tileSource.getName()); + if (f != null && f.exists() && f.isDirectory() && Algorithms.removeAllFiles(f)) { + appliedItems.add(tileSource); + } + } + } + } else { + for (ITileSource tileSource : duplicateItems) { + appliedItems.add(renameItem(tileSource)); + } + } + for (ITileSource tileSource : appliedItems) { + if (tileSource instanceof TileSourceManager.TileSourceTemplate) { + app.getSettings().installTileSource((TileSourceManager.TileSourceTemplate) tileSource); + } else if (tileSource instanceof SQLiteTileSource) { + ((SQLiteTileSource) tileSource).createDataBase(); + } + } + } + } + + @NonNull + @Override + public ITileSource renameItem(@NonNull ITileSource item) { + int number = 0; + while (true) { + number++; + if (item instanceof SQLiteTileSource) { + SQLiteTileSource oldItem = (SQLiteTileSource) item; + SQLiteTileSource renamedItem = new SQLiteTileSource( + oldItem, + oldItem.getName() + "_" + number, + app); + if (!isDuplicate(renamedItem)) { + return renamedItem; + } + } else if (item instanceof TileSourceManager.TileSourceTemplate) { + TileSourceManager.TileSourceTemplate oldItem = (TileSourceManager.TileSourceTemplate) item; + oldItem.setName(oldItem.getName() + "_" + number); + if (!isDuplicate(oldItem)) { + return oldItem; + } + } + } + } + + @Override + public boolean isDuplicate(@NonNull ITileSource item) { + for (String name : existingItemsNames) { + if (name.equals(item.getName())) { + return true; + } + } + return false; + } + + @NonNull + @Override + public String getName() { + return "map_sources"; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return "map_sources"; + } + + @Override + public boolean shouldReadOnCollecting() { + return true; + } + + @Override + void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { + try { + if (!json.has("items")) { + return; + } + JSONArray jsonArray = json.getJSONArray("items"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject object = jsonArray.getJSONObject(i); + boolean sql = object.optBoolean("sql"); + String name = object.optString("name"); + int minZoom = object.optInt("minZoom"); + int maxZoom = object.optInt("maxZoom"); + String url = object.optString("url"); + String randoms = object.optString("randoms"); + boolean ellipsoid = object.optBoolean("ellipsoid", false); + boolean invertedY = object.optBoolean("inverted_y", false); + String referer = object.optString("referer"); + String userAgent = object.optString("userAgent"); + boolean timeSupported = object.optBoolean("timesupported", false); + long expire = object.optLong("expire", -1); + boolean inversiveZoom = object.optBoolean("inversiveZoom", false); + String ext = object.optString("ext"); + int tileSize = object.optInt("tileSize"); + int bitDensity = object.optInt("bitDensity"); + int avgSize = object.optInt("avgSize"); + String rule = object.optString("rule"); + + if (expire > 0 && expire < 3600000) { + expire = expire * 60 * 1000L; + } + + ITileSource template; + if (!sql) { + TileSourceManager.TileSourceTemplate tileSourceTemplate = new TileSourceManager.TileSourceTemplate(name, url, ext, maxZoom, minZoom, tileSize, bitDensity, avgSize); + tileSourceTemplate.setRule(rule); + tileSourceTemplate.setRandoms(randoms); + tileSourceTemplate.setReferer(referer); + tileSourceTemplate.setUserAgent(userAgent); + tileSourceTemplate.setEllipticYTile(ellipsoid); + tileSourceTemplate.setInvertedYTile(invertedY); + tileSourceTemplate.setExpirationTimeMillis(timeSupported ? expire : -1); + + template = tileSourceTemplate; + } else { + template = new SQLiteTileSource(app, name, minZoom, maxZoom, url, randoms, ellipsoid, invertedY, referer, userAgent, timeSupported, expire, inversiveZoom, rule); + } + items.add(template); + } + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); + throw new IllegalArgumentException("Json parse error", e); + } + } + + @Override + void writeItemsToJson(@NonNull JSONObject json) { + JSONArray jsonArray = new JSONArray(); + if (!items.isEmpty()) { + try { + for (ITileSource template : items) { + JSONObject jsonObject = new JSONObject(); + boolean sql = template instanceof SQLiteTileSource; + jsonObject.put("sql", sql); + jsonObject.put("name", template.getName()); + jsonObject.put("minZoom", template.getMinimumZoomSupported()); + jsonObject.put("maxZoom", template.getMaximumZoomSupported()); + jsonObject.put("url", template.getUrlTemplate()); + jsonObject.put("randoms", template.getRandoms()); + jsonObject.put("ellipsoid", template.isEllipticYTile()); + jsonObject.put("inverted_y", template.isInvertedYTile()); + jsonObject.put("referer", template.getReferer()); + jsonObject.put("userAgent", template.getUserAgent()); + jsonObject.put("timesupported", template.isTimeSupported()); + jsonObject.put("expire", template.getExpirationTimeMinutes()); + jsonObject.put("inversiveZoom", template.getInversiveZoom()); + jsonObject.put("ext", template.getTileFormat()); + jsonObject.put("tileSize", template.getTileSize()); + jsonObject.put("bitDensity", template.getBitDensity()); + jsonObject.put("avgSize", template.getAvgSize()); + jsonObject.put("rule", template.getRule()); + jsonArray.put(jsonObject); + } + json.put("items", jsonArray); + + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); + SettingsHelper.LOG.error("Failed write to json", e); + } + } + } + + @Nullable + @Override + SettingsItemReader getReader() { + return getJsonReader(); + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItem.java new file mode 100644 index 0000000000..033b856e05 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItem.java @@ -0,0 +1,33 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.settings.backend.OsmandSettings; + +import org.json.JSONException; +import org.json.JSONObject; + +public abstract class OsmandSettingsItem extends SettingsItem { + + private OsmandSettings settings; + + protected OsmandSettingsItem(@NonNull OsmandSettings settings) { + super(settings.getContext()); + this.settings = settings; + } + + protected OsmandSettingsItem(@NonNull OsmandSettings settings, @Nullable OsmandSettingsItem baseItem) { + super(settings.getContext(), baseItem); + this.settings = settings; + } + + protected OsmandSettingsItem(@NonNull SettingsItemType type, @NonNull OsmandSettings settings, @NonNull JSONObject json) throws JSONException { + super(settings.getContext(), json); + this.settings = settings; + } + + public OsmandSettings getSettings() { + return settings; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItemReader.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItemReader.java new file mode 100644 index 0000000000..0267e69471 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItemReader.java @@ -0,0 +1,78 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; + +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Iterator; +import java.util.Map; + +public abstract class OsmandSettingsItemReader extends SettingsItemReader { + + private OsmandSettings settings; + + public OsmandSettingsItemReader(@NonNull T item, @NonNull OsmandSettings settings) { + super(item); + this.settings = settings; + } + + protected abstract void readPreferenceFromJson(@NonNull OsmandPreference preference, + @NonNull JSONObject json) throws JSONException; + + @Override + public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { + StringBuilder buf = new StringBuilder(); + try { + BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + String str; + while ((str = in.readLine()) != null) { + buf.append(str); + } + } catch (IOException e) { + throw new IOException("Cannot read json body", e); + } + String jsonStr = buf.toString(); + if (Algorithms.isEmpty(jsonStr)) { + throw new IllegalArgumentException("Cannot find json body"); + } + final JSONObject json; + try { + json = new JSONObject(jsonStr); + } catch (JSONException e) { + throw new IllegalArgumentException("Json parse error", e); + } + readPreferencesFromJson(json); + } + + void readPreferencesFromJson(final JSONObject json) { + settings.getContext().runInUIThread(new Runnable() { + @Override + public void run() { + Map> prefs = settings.getRegisteredPreferences(); + Iterator iter = json.keys(); + while (iter.hasNext()) { + String key = iter.next(); + OsmandPreference p = prefs.get(key); + if (p != null) { + try { + readPreferenceFromJson(p, json); + } catch (Exception e) { + SettingsHelper.LOG.error("Failed to read preference: " + key, e); + } + } else { + SettingsHelper.LOG.warn("No preference while importing settings: " + key); + } + } + } + }); + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItemWriter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItemWriter.java new file mode 100644 index 0000000000..810ae90f31 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/OsmandSettingsItemWriter.java @@ -0,0 +1,49 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; + +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Map; + +public abstract class OsmandSettingsItemWriter extends SettingsItemWriter { + + private OsmandSettings settings; + + public OsmandSettingsItemWriter(@NonNull T item, @NonNull OsmandSettings settings) { + super(item); + this.settings = settings; + } + + protected abstract void writePreferenceToJson(@NonNull OsmandPreference preference, + @NonNull JSONObject json) throws JSONException; + + @Override + public boolean writeToStream(@NonNull OutputStream outputStream) throws IOException { + JSONObject json = new JSONObject(); + Map> prefs = settings.getRegisteredPreferences(); + for (OsmandPreference pref : prefs.values()) { + try { + writePreferenceToJson(pref, json); + } catch (JSONException e) { + SettingsHelper.LOG.error("Failed to write preference: " + pref.getId(), e); + } + } + if (json.length() > 0) { + try { + String s = json.toString(2); + outputStream.write(s.getBytes("UTF-8")); + } catch (JSONException e) { + SettingsHelper.LOG.error("Failed to write json to stream", e); + } + return true; + } + return false; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/PluginSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PluginSettingsItem.java new file mode 100644 index 0000000000..32d6a04e22 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PluginSettingsItem.java @@ -0,0 +1,116 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.CustomOsmandPlugin; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class PluginSettingsItem extends SettingsItem { + + private CustomOsmandPlugin plugin; + private List pluginDependentItems; + + PluginSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + @Override + protected void init() { + super.init(); + pluginDependentItems = new ArrayList<>(); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.PLUGIN; + } + + @NonNull + @Override + public String getName() { + return plugin.getId(); + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return plugin.getName(); + } + + @NonNull + @Override + public String getDefaultFileName() { + return getName(); + } + + public CustomOsmandPlugin getPlugin() { + return plugin; + } + + public List getPluginDependentItems() { + return pluginDependentItems; + } + + @Override + public boolean exists() { + return OsmandPlugin.getPlugin(getPluginId()) != null; + } + + @Override + public void apply() { + if (shouldReplace || !exists()) { + for (SettingsItem item : pluginDependentItems) { + if (item instanceof FileSettingsItem) { + FileSettingsItem fileItem = (FileSettingsItem) item; + if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.RENDERING_STYLE) { + plugin.addRenderer(fileItem.getName()); + } else if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.ROUTING_CONFIG) { + plugin.addRouter(fileItem.getName()); + } else if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.OTHER) { + plugin.setResourceDirName(item.getFileName()); + } + } else if (item instanceof SuggestedDownloadsItem) { + plugin.updateSuggestedDownloads(((SuggestedDownloadsItem) item).getItems()); + } else if (item instanceof DownloadsItem) { + plugin.updateDownloadItems(((DownloadsItem) item).getItems()); + } + } + OsmandPlugin.addCustomPlugin(app, plugin); + } + } + + @Override + void readFromJson(@NonNull JSONObject json) throws JSONException { + super.readFromJson(json); + plugin = new CustomOsmandPlugin(app, json); + } + + @Override + void writeToJson(@NonNull JSONObject json) throws JSONException { + super.writeToJson(json); + plugin.writeAdditionalDataToJson(json); + } + + @Nullable + @Override + SettingsItemReader getReader() { + return null; + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java new file mode 100644 index 0000000000..8d1c464761 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java @@ -0,0 +1,178 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import net.osmand.osm.MapPoiTypes; +import net.osmand.osm.PoiCategory; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.poi.PoiUIFilter; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +public class PoiUiFiltersSettingsItem extends CollectionSettingsItem { + + public PoiUiFiltersSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { + super(app, null, items); + } + + public PoiUiFiltersSettingsItem(@NonNull OsmandApplication app, @Nullable PoiUiFiltersSettingsItem baseItem, @NonNull List items) { + super(app, baseItem, items); + } + + PoiUiFiltersSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + @Override + protected void init() { + super.init(); + existingItems = app.getPoiFilters().getUserDefinedPoiFilters(false); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.POI_UI_FILTERS; + } + + @Override + public void apply() { + List newItems = getNewItems(); + if (!newItems.isEmpty() || !duplicateItems.isEmpty()) { + appliedItems = new ArrayList<>(newItems); + + for (PoiUIFilter duplicate : duplicateItems) { + appliedItems.add(shouldReplace ? duplicate : renameItem(duplicate)); + } + for (PoiUIFilter filter : appliedItems) { + app.getPoiFilters().createPoiFilter(filter, false); + } + app.getSearchUICore().refreshCustomPoiFilters(); + } + } + + @Override + public boolean isDuplicate(@NonNull PoiUIFilter item) { + String savedName = item.getName(); + for (PoiUIFilter filter : existingItems) { + if (filter.getName().equals(savedName)) { + return true; + } + } + return false; + } + + @NonNull + @Override + public PoiUIFilter renameItem(@NonNull PoiUIFilter item) { + int number = 0; + while (true) { + number++; + PoiUIFilter renamedItem = new PoiUIFilter(item, + item.getName() + "_" + number, + item.getFilterId() + "_" + number); + if (!isDuplicate(renamedItem)) { + return renamedItem; + } + } + } + + @NonNull + @Override + public String getName() { + return "poi_ui_filters"; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return "poi_ui_filters"; + } + + @Override + public boolean shouldReadOnCollecting() { + return true; + } + + @Override + void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { + try { + if (!json.has("items")) { + return; + } + JSONArray jsonArray = json.getJSONArray("items"); + Gson gson = new Gson(); + Type type = new TypeToken>>() { + }.getType(); + MapPoiTypes poiTypes = app.getPoiTypes(); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject object = jsonArray.getJSONObject(i); + String name = object.getString("name"); + String filterId = object.getString("filterId"); + String acceptedTypesString = object.getString("acceptedTypes"); + HashMap> acceptedTypes = gson.fromJson(acceptedTypesString, type); + Map> acceptedTypesDone = new HashMap<>(); + for (Map.Entry> mapItem : acceptedTypes.entrySet()) { + final PoiCategory a = poiTypes.getPoiCategoryByName(mapItem.getKey()); + acceptedTypesDone.put(a, mapItem.getValue()); + } + PoiUIFilter filter = new PoiUIFilter(name, filterId, acceptedTypesDone, app); + items.add(filter); + } + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); + throw new IllegalArgumentException("Json parse error", e); + } + } + + @Override + void writeItemsToJson(@NonNull JSONObject json) { + JSONArray jsonArray = new JSONArray(); + Gson gson = new Gson(); + Type type = new TypeToken>>() { + }.getType(); + if (!items.isEmpty()) { + try { + for (PoiUIFilter filter : items) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("name", filter.getName()); + jsonObject.put("filterId", filter.getFilterId()); + jsonObject.put("acceptedTypes", gson.toJson(filter.getAcceptedTypes(), type)); + jsonArray.put(jsonObject); + } + json.put("items", jsonArray); + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); + SettingsHelper.LOG.error("Failed write to json", e); + } + } + } + + @Nullable + @Override + SettingsItemReader getReader() { + return getJsonReader(); + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/ProfileSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/ProfileSettingsItem.java new file mode 100644 index 0000000000..309fe4407c --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/ProfileSettingsItem.java @@ -0,0 +1,312 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.IndexConstants; +import net.osmand.plus.CustomOsmandPlugin; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.router.GeneralRouter; +import net.osmand.util.Algorithms; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ProfileSettingsItem extends OsmandSettingsItem { + + private ApplicationMode appMode; + private ApplicationMode.ApplicationModeBuilder builder; + private ApplicationMode.ApplicationModeBean modeBean; + + private JSONObject additionalPrefsJson; + private Set appModeBeanPrefsIds; + + public ProfileSettingsItem(@NonNull OsmandApplication app, @NonNull ApplicationMode appMode) { + super(app.getSettings()); + this.appMode = appMode; + } + + public ProfileSettingsItem(@NonNull OsmandApplication app, @Nullable ProfileSettingsItem baseItem, @NonNull ApplicationMode.ApplicationModeBean modeBean) { + super(app.getSettings(), baseItem); + this.modeBean = modeBean; + builder = ApplicationMode.fromModeBean(app, modeBean); + appMode = builder.getApplicationMode(); + } + + public ProfileSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(SettingsItemType.PROFILE, app.getSettings(), json); + } + + @Override + protected void init() { + super.init(); + appModeBeanPrefsIds = new HashSet<>(Arrays.asList(getAppModeBeanPrefsIds())); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.PROFILE; + } + + public ApplicationMode getAppMode() { + return appMode; + } + + public ApplicationMode.ApplicationModeBean getModeBean() { + return modeBean; + } + + @NonNull + @Override + public String getName() { + return appMode.getStringKey(); + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + if (appMode.isCustomProfile()) { + return modeBean.userProfileName; + } else if (appMode.getNameKeyResource() != -1) { + return ctx.getString(appMode.getNameKeyResource()); + } else { + return getName(); + } + } + + @NonNull + @Override + public String getDefaultFileName() { + return "profile_" + getName() + getDefaultFileExtension(); + } + + @Override + void readFromJson(@NonNull JSONObject json) throws JSONException { + super.readFromJson(json); + String appModeJson = json.getString("appMode"); + modeBean = ApplicationMode.fromJson(appModeJson); + builder = ApplicationMode.fromModeBean(app, modeBean); + ApplicationMode appMode = builder.getApplicationMode(); + if (!appMode.isCustomProfile()) { + appMode = ApplicationMode.valueOfStringKey(appMode.getStringKey(), appMode); + } + this.appMode = appMode; + } + + @Override + void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { + additionalPrefsJson = json.optJSONObject("prefs"); + } + + @Override + public boolean exists() { + return builder != null && ApplicationMode.valueOfStringKey(getName(), null) != null; + } + + private void renameProfile() { + List values = ApplicationMode.allPossibleValues(); + if (Algorithms.isEmpty(modeBean.userProfileName)) { + ApplicationMode appMode = ApplicationMode.valueOfStringKey(modeBean.stringKey, null); + if (appMode != null) { + modeBean.userProfileName = app.getString(appMode.getNameKeyResource()); + } + } + int number = 0; + while (true) { + number++; + String key = modeBean.stringKey + "_" + number; + String name = modeBean.userProfileName + '_' + number; + if (ApplicationMode.valueOfStringKey(key, null) == null && isNameUnique(values, name)) { + modeBean.userProfileName = name; + modeBean.stringKey = key; + break; + } + } + } + + private boolean isNameUnique(List values, String name) { + for (ApplicationMode mode : values) { + if (mode.getUserProfileName().equals(name)) { + return false; + } + } + return true; + } + + @Override + public void apply() { + if (!appMode.isCustomProfile() && !shouldReplace) { + ApplicationMode parent = ApplicationMode.valueOfStringKey(modeBean.stringKey, null); + renameProfile(); + ApplicationMode.ApplicationModeBuilder builder = ApplicationMode + .createCustomMode(parent, modeBean.stringKey, app) + .setIconResName(modeBean.iconName) + .setUserProfileName(modeBean.userProfileName) + .setRoutingProfile(modeBean.routingProfile) + .setRouteService(modeBean.routeService) + .setIconColor(modeBean.iconColor) + .setLocationIcon(modeBean.locIcon) + .setNavigationIcon(modeBean.navIcon); + app.getSettings().copyPreferencesFromProfile(parent, builder.getApplicationMode()); + appMode = ApplicationMode.saveProfile(builder, app); + } else if (!shouldReplace && exists()) { + renameProfile(); + builder = ApplicationMode.fromModeBean(app, modeBean); + appMode = ApplicationMode.saveProfile(builder, app); + } else { + builder = ApplicationMode.fromModeBean(app, modeBean); + appMode = ApplicationMode.saveProfile(builder, app); + } + ApplicationMode.changeProfileAvailability(appMode, true, app); + } + + public void applyAdditionalPrefs() { + if (additionalPrefsJson != null) { + updatePluginResPrefs(); + + SettingsItemReader reader = getReader(); + if (reader instanceof OsmandSettingsItemReader) { + ((OsmandSettingsItemReader) reader).readPreferencesFromJson(additionalPrefsJson); + } + } + } + + private void updatePluginResPrefs() { + String pluginId = getPluginId(); + if (Algorithms.isEmpty(pluginId)) { + return; + } + OsmandPlugin plugin = OsmandPlugin.getPlugin(pluginId); + if (plugin instanceof CustomOsmandPlugin) { + CustomOsmandPlugin customPlugin = (CustomOsmandPlugin) plugin; + String resDirPath = IndexConstants.PLUGINS_DIR + pluginId + "/" + customPlugin.getResourceDirName(); + + for (Iterator it = additionalPrefsJson.keys(); it.hasNext(); ) { + try { + String prefId = it.next(); + Object value = additionalPrefsJson.get(prefId); + if (value instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) value; + for (Iterator iterator = jsonObject.keys(); iterator.hasNext(); ) { + String key = iterator.next(); + Object val = jsonObject.get(key); + if (val instanceof String) { + val = checkPluginResPath((String) val, resDirPath); + } + jsonObject.put(key, val); + } + } else if (value instanceof String) { + value = checkPluginResPath((String) value, resDirPath); + additionalPrefsJson.put(prefId, value); + } + } catch (JSONException e) { + SettingsHelper.LOG.error(e); + } + } + } + } + + private String checkPluginResPath(String path, String resDirPath) { + if (path.startsWith("@")) { + return resDirPath + "/" + path.substring(1); + } + return path; + } + + @Override + void writeToJson(@NonNull JSONObject json) throws JSONException { + super.writeToJson(json); + json.put("appMode", new JSONObject(appMode.toJson())); + } + + @Nullable + @Override + SettingsItemReader getReader() { + return new OsmandSettingsItemReader(this, getSettings()) { + @Override + protected void readPreferenceFromJson(@NonNull OsmandPreference preference, @NonNull JSONObject json) throws JSONException { + if (!appModeBeanPrefsIds.contains(preference.getId())) { + preference.readFromJson(json, appMode); + } + } + + @Override + void readPreferencesFromJson(final JSONObject json) { + getSettings().getContext().runInUIThread(new Runnable() { + @Override + public void run() { + OsmandSettings settings = getSettings(); + Map> prefs = settings.getRegisteredPreferences(); + Iterator iter = json.keys(); + while (iter.hasNext()) { + String key = iter.next(); + OsmandPreference p = prefs.get(key); + if (p == null) { + if (OsmandSettings.isRoutingPreference(key)) { + p = settings.registerStringPreference(key, ""); + } + } + if (p != null) { + try { + readPreferenceFromJson(p, json); + if (OsmandSettings.isRoutingPreference(p.getId())) { + if (p.getId().endsWith(GeneralRouter.USE_SHORTEST_WAY)) { + settings.FAST_ROUTE_MODE.setModeValue(appMode, + !settings.getCustomRoutingBooleanProperty(GeneralRouter.USE_SHORTEST_WAY, false).getModeValue(appMode)); + } + } + } catch (Exception e) { + SettingsHelper.LOG.error("Failed to read preference: " + key, e); + } + } else { + SettingsHelper.LOG.warn("No preference while importing settings: " + key); + } + } + } + }); + } + }; + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return new OsmandSettingsItemWriter(this, getSettings()) { + @Override + protected void writePreferenceToJson(@NonNull OsmandPreference preference, @NonNull JSONObject json) throws JSONException { + if (!appModeBeanPrefsIds.contains(preference.getId())) { + preference.writeToJson(json, appMode); + } + } + }; + } + + private String[] getAppModeBeanPrefsIds() { + OsmandSettings settings = app.getSettings(); + return new String[] { + settings.ICON_COLOR.getId(), + settings.ICON_RES_NAME.getId(), + settings.PARENT_APP_MODE.getId(), + settings.ROUTING_PROFILE.getId(), + settings.ROUTE_SERVICE.getId(), + settings.USER_PROFILE_NAME.getId(), + settings.LOCATION_ICON.getId(), + settings.NAVIGATION_ICON.getId(), + settings.APP_MODE_ORDER.getId() + }; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/QuickActionsSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/QuickActionsSettingsItem.java new file mode 100644 index 0000000000..b2e597ab60 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/QuickActionsSettingsItem.java @@ -0,0 +1,183 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.quickaction.QuickAction; +import net.osmand.plus.quickaction.QuickActionRegistry; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class QuickActionsSettingsItem extends CollectionSettingsItem { + + private QuickActionRegistry actionRegistry; + + public QuickActionsSettingsItem(@NonNull OsmandApplication app, @NonNull List items) { + super(app, null, items); + } + + public QuickActionsSettingsItem(@NonNull OsmandApplication app, @Nullable QuickActionsSettingsItem baseItem, @NonNull List items) { + super(app, baseItem, items); + } + + QuickActionsSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + @Override + protected void init() { + super.init(); + actionRegistry = app.getQuickActionRegistry(); + existingItems = actionRegistry.getQuickActions(); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.QUICK_ACTIONS; + } + + @Override + public boolean isDuplicate(@NonNull QuickAction item) { + return !actionRegistry.isNameUnique(item, app); + } + + @NonNull + @Override + public QuickAction renameItem(@NonNull QuickAction item) { + return actionRegistry.generateUniqueName(item, app); + } + + @Override + public void apply() { + List newItems = getNewItems(); + if (!newItems.isEmpty() || !duplicateItems.isEmpty()) { + appliedItems = new ArrayList<>(newItems); + List newActions = new ArrayList<>(existingItems); + if (!duplicateItems.isEmpty()) { + if (shouldReplace) { + for (QuickAction duplicateItem : duplicateItems) { + for (QuickAction savedAction : existingItems) { + if (duplicateItem.getName(app).equals(savedAction.getName(app))) { + newActions.remove(savedAction); + } + } + } + } else { + for (QuickAction duplicateItem : duplicateItems) { + renameItem(duplicateItem); + } + } + appliedItems.addAll(duplicateItems); + } + newActions.addAll(appliedItems); + actionRegistry.updateQuickActions(newActions); + } + } + + @Override + public boolean shouldReadOnCollecting() { + return true; + } + + @NonNull + @Override + public String getName() { + return "quick_actions"; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return "quick_actions"; + } + + @Override + void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { + try { + if (!json.has("items")) { + return; + } + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + QuickActionRegistry quickActionRegistry = app.getQuickActionRegistry(); + JSONArray itemsJson = json.getJSONArray("items"); + for (int i = 0; i < itemsJson.length(); i++) { + JSONObject object = itemsJson.getJSONObject(i); + String name = object.getString("name"); + QuickAction quickAction = null; + if (object.has("actionType")) { + quickAction = quickActionRegistry.newActionByStringType(object.getString("actionType")); + } else if (object.has("type")) { + quickAction = quickActionRegistry.newActionByType(object.getInt("type")); + } + if (quickAction != null) { + String paramsString = object.getString("params"); + HashMap params = gson.fromJson(paramsString, type); + + if (!name.isEmpty()) { + quickAction.setName(name); + } + quickAction.setParams(params); + items.add(quickAction); + } else { + warnings.add(app.getString(R.string.settings_item_read_error, name)); + } + } + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); + throw new IllegalArgumentException("Json parse error", e); + } + } + + @Override + void writeItemsToJson(@NonNull JSONObject json) { + JSONArray jsonArray = new JSONArray(); + Gson gson = new Gson(); + Type type = new TypeToken>() { + }.getType(); + if (!items.isEmpty()) { + try { + for (QuickAction action : items) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("name", action.hasCustomName(app) + ? action.getName(app) : ""); + jsonObject.put("actionType", action.getActionType().getStringId()); + jsonObject.put("params", gson.toJson(action.getParams(), type)); + jsonArray.put(jsonObject); + } + json.put("items", jsonArray); + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); + SettingsHelper.LOG.error("Failed write to json", e); + } + } + } + + @Nullable + @Override + SettingsItemReader getReader() { + return getJsonReader(); + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/ResourcesSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/ResourcesSettingsItem.java new file mode 100644 index 0000000000..194125e7b9 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/ResourcesSettingsItem.java @@ -0,0 +1,72 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; +import net.osmand.util.Algorithms; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; + +public class ResourcesSettingsItem extends FileSettingsItem { + + ResourcesSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + shouldReplace = true; + String fileName = getFileName(); + if (!Algorithms.isEmpty(fileName) && !fileName.endsWith(File.separator)) { + this.fileName = fileName + File.separator; + } + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.RESOURCES; + } + + @Override + void readFromJson(@NonNull JSONObject json) throws JSONException { + subtype = FileSubtype.OTHER; + super.readFromJson(json); + } + + @Override + void writeToJson(@NonNull JSONObject json) throws JSONException { + super.writeToJson(json); + String fileName = getFileName(); + if (!Algorithms.isEmpty(fileName)) { + if (fileName.endsWith(File.separator)) { + fileName = fileName.substring(0, fileName.length() - 1); + } + json.put("file", fileName); + } + } + + @Override + public boolean applyFileName(@NonNull String fileName) { + if (fileName.endsWith(File.separator)) { + return false; + } + String itemFileName = getFileName(); + if (itemFileName != null && itemFileName.endsWith(File.separator)) { + if (fileName.startsWith(itemFileName)) { + this.file = new File(getPluginPath(), fileName); + return true; + } else { + return false; + } + } else { + return super.applyFileName(fileName); + } + } + + @Nullable + @Override + public SettingsItemWriter getWriter() { + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsExporter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsExporter.java new file mode 100644 index 0000000000..ccc23b5416 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsExporter.java @@ -0,0 +1,91 @@ +package net.osmand.plus.settings.backend.backup; + +import net.osmand.util.Algorithms; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +class SettingsExporter { + + private Map items; + private Map additionalParams; + private boolean exportItemsFiles; + + SettingsExporter(boolean exportItemsFiles) { + this.exportItemsFiles = exportItemsFiles; + items = new LinkedHashMap<>(); + additionalParams = new LinkedHashMap<>(); + } + + void addSettingsItem(SettingsItem item) throws IllegalArgumentException { + if (items.containsKey(item.getName())) { + throw new IllegalArgumentException("Already has such item: " + item.getName()); + } + items.put(item.getName(), item); + } + + void addAdditionalParam(String key, String value) { + additionalParams.put(key, value); + } + + void exportSettings(File file) throws JSONException, IOException { + JSONObject json = createItemsJson(); + OutputStream os = new BufferedOutputStream(new FileOutputStream(file), SettingsHelper.BUFFER); + ZipOutputStream zos = new ZipOutputStream(os); + try { + ZipEntry entry = new ZipEntry("items.json"); + zos.putNextEntry(entry); + zos.write(json.toString(2).getBytes("UTF-8")); + zos.closeEntry(); + if (exportItemsFiles) { + writeItemFiles(zos); + } + zos.flush(); + zos.finish(); + } finally { + Algorithms.closeStream(zos); + Algorithms.closeStream(os); + } + } + + private void writeItemFiles(ZipOutputStream zos) throws IOException { + for (SettingsItem item : items.values()) { + SettingsItemWriter writer = item.getWriter(); + if (writer != null) { + String fileName = item.getFileName(); + if (Algorithms.isEmpty(fileName)) { + fileName = item.getDefaultFileName(); + } + ZipEntry entry = new ZipEntry(fileName); + zos.putNextEntry(entry); + writer.writeToStream(zos); + zos.closeEntry(); + } + } + } + + private JSONObject createItemsJson() throws JSONException { + JSONObject json = new JSONObject(); + json.put("version", SettingsHelper.VERSION); + for (Map.Entry param : additionalParams.entrySet()) { + json.put(param.getKey(), param.getValue()); + } + JSONArray itemsJson = new JSONArray(); + for (SettingsItem item : items.values()) { + itemsJson.put(new JSONObject(item.toJson())); + } + json.put("items", itemsJson); + return json; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java new file mode 100644 index 0000000000..530147b84f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java @@ -0,0 +1,643 @@ +package net.osmand.plus.settings.backend.backup; + +import android.annotation.SuppressLint; +import android.os.AsyncTask; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.AndroidUtils; +import net.osmand.IndexConstants; +import net.osmand.PlatformUtil; +import net.osmand.data.LatLon; +import net.osmand.map.ITileSource; +import net.osmand.map.TileSourceManager; +import net.osmand.map.TileSourceManager.TileSourceTemplate; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.SQLiteTileSource; +import net.osmand.plus.audionotes.AudioVideoNotesPlugin; +import net.osmand.plus.audionotes.AudioVideoNotesPlugin.Recording; +import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; +import net.osmand.plus.helpers.GpxUiHelper; +import net.osmand.plus.helpers.GpxUiHelper.GPXInfo; +import net.osmand.plus.poi.PoiUIFilter; +import net.osmand.plus.quickaction.QuickAction; +import net.osmand.plus.quickaction.QuickActionRegistry; +import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; +import net.osmand.plus.settings.backend.ExportSettingsType; + +import org.apache.commons.logging.Log; +import org.json.JSONException; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static net.osmand.IndexConstants.OSMAND_SETTINGS_FILE_EXT; + +/* + Usage: + + SettingsHelper helper = app.getSettingsHelper(); + File file = new File(app.getAppPath(null), "settings.zip"); + + List items = new ArrayList<>(); + items.add(new GlobalSettingsItem(app.getSettings())); + items.add(new ProfileSettingsItem(app.getSettings(), ApplicationMode.DEFAULT)); + items.add(new ProfileSettingsItem(app.getSettings(), ApplicationMode.CAR)); + items.add(new ProfileSettingsItem(app.getSettings(), ApplicationMode.PEDESTRIAN)); + items.add(new ProfileSettingsItem(app.getSettings(), ApplicationMode.BICYCLE)); + items.add(new FileSettingsItem(app, new File(app.getAppPath(GPX_INDEX_DIR), "Day 2.gpx"))); + items.add(new FileSettingsItem(app, new File(app.getAppPath(GPX_INDEX_DIR), "Day 3.gpx"))); + items.add(new FileSettingsItem(app, new File(app.getAppPath(RENDERERS_DIR), "default.render.xml"))); + items.add(new DataSettingsItem(new byte[] {'t', 'e', 's', 't', '1'}, "data1")); + items.add(new DataSettingsItem(new byte[] {'t', 'e', 's', 't', '2'}, "data2")); + + helper.exportSettings(file, items); + + helper.importSettings(file); + */ + +public class SettingsHelper { + + public static final int VERSION = 1; + + public static final String SETTINGS_TYPE_LIST_KEY = "settings_type_list_key"; + public static final String REPLACE_KEY = "replace"; + public static final String SETTINGS_LATEST_CHANGES_KEY = "settings_latest_changes"; + public static final String SETTINGS_VERSION_KEY = "settings_version"; + + public static final int BUFFER = 1024; + + protected static final Log LOG = PlatformUtil.getLog(SettingsHelper.class); + + private OsmandApplication app; + + private ImportAsyncTask importTask; + private Map exportAsyncTasks = new HashMap<>(); + + public interface SettingsImportListener { + void onSettingsImportFinished(boolean succeed, @NonNull List items); + } + + public interface SettingsCollectListener { + void onSettingsCollectFinished(boolean succeed, boolean empty, @NonNull List items); + } + + public interface CheckDuplicatesListener { + void onDuplicatesChecked(@NonNull List duplicates, List items); + } + + public interface SettingsExportListener { + void onSettingsExportFinished(@NonNull File file, boolean succeed); + } + + public enum ImportType { + COLLECT, + CHECK_DUPLICATES, + IMPORT + } + + public SettingsHelper(@NonNull OsmandApplication app) { + this.app = app; + } + + @Nullable + public ImportAsyncTask getImportTask() { + return importTask; + } + + @Nullable + public ImportType getImportTaskType() { + ImportAsyncTask importTask = this.importTask; + return importTask != null ? importTask.getImportType() : null; + } + + public boolean isImportDone() { + ImportAsyncTask importTask = this.importTask; + return importTask == null || importTask.isImportDone(); + } + + public boolean isFileExporting(File file) { + return exportAsyncTasks.containsKey(file); + } + + public void updateExportListener(File file, SettingsExportListener listener) { + ExportAsyncTask exportAsyncTask = exportAsyncTasks.get(file); + if (exportAsyncTask != null) { + exportAsyncTask.listener = listener; + } + } + + private void finishImport(@Nullable SettingsImportListener listener, boolean success, @NonNull List items) { + importTask = null; + List warnings = new ArrayList<>(); + for (SettingsItem item : items) { + warnings.addAll(item.getWarnings()); + } + if (!warnings.isEmpty()) { + app.showToastMessage(AndroidUtils.formatWarnings(warnings).toString()); + } + if (listener != null) { + listener.onSettingsImportFinished(success, items); + } + } + + @SuppressLint("StaticFieldLeak") + private class ImportItemsAsyncTask extends AsyncTask { + + private SettingsImporter importer; + private File file; + private SettingsImportListener listener; + private List items; + + ImportItemsAsyncTask(@NonNull File file, + @Nullable SettingsImportListener listener, + @NonNull List items) { + importer = new SettingsImporter(app); + this.file = file; + this.listener = listener; + this.items = items; + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + importer.importItems(file, items); + return true; + } catch (IllegalArgumentException e) { + LOG.error("Failed to import items from: " + file.getName(), e); + } catch (IOException e) { + LOG.error("Failed to import items from: " + file.getName(), e); + } + return false; + } + + @Override + protected void onPostExecute(Boolean success) { + finishImport(listener, success, items); + } + } + + @SuppressLint("StaticFieldLeak") + private class ExportAsyncTask extends AsyncTask { + + private SettingsExporter exporter; + private File file; + private SettingsExportListener listener; + + ExportAsyncTask(@NonNull File settingsFile, + @Nullable SettingsExportListener listener, + @NonNull List items, boolean exportItemsFiles) { + this.file = settingsFile; + this.listener = listener; + this.exporter = new SettingsExporter(exportItemsFiles); + for (SettingsItem item : items) { + exporter.addSettingsItem(item); + } + } + + @Override + protected Boolean doInBackground(Void... voids) { + try { + exporter.exportSettings(file); + return true; + } catch (JSONException e) { + LOG.error("Failed to export items to: " + file.getName(), e); + } catch (IOException e) { + LOG.error("Failed to export items to: " + file.getName(), e); + } + return false; + } + + @Override + protected void onPostExecute(Boolean success) { + exportAsyncTasks.remove(file); + if (listener != null) { + listener.onSettingsExportFinished(file, success); + } + } + } + + @SuppressLint("StaticFieldLeak") + public class ImportAsyncTask extends AsyncTask> { + + private File file; + private String latestChanges; + private int version; + + private SettingsImportListener importListener; + private SettingsCollectListener collectListener; + private CheckDuplicatesListener duplicatesListener; + private SettingsImporter importer; + + private List items = new ArrayList<>(); + private List selectedItems = new ArrayList<>(); + private List duplicates; + + private ImportType importType; + private boolean importDone; + + ImportAsyncTask(@NonNull File file, String latestChanges, int version, @Nullable SettingsCollectListener collectListener) { + this.file = file; + this.collectListener = collectListener; + this.latestChanges = latestChanges; + this.version = version; + importer = new SettingsImporter(app); + importType = ImportType.COLLECT; + } + + ImportAsyncTask(@NonNull File file, @NonNull List items, String latestChanges, int version, @Nullable SettingsImportListener importListener) { + this.file = file; + this.importListener = importListener; + this.items = items; + this.latestChanges = latestChanges; + this.version = version; + importer = new SettingsImporter(app); + importType = ImportType.IMPORT; + } + + ImportAsyncTask(@NonNull File file, @NonNull List items, @NonNull List selectedItems, @Nullable CheckDuplicatesListener duplicatesListener) { + this.file = file; + this.items = items; + this.duplicatesListener = duplicatesListener; + this.selectedItems = selectedItems; + importer = new SettingsImporter(app); + importType = ImportType.CHECK_DUPLICATES; + } + + @Override + protected void onPreExecute() { + ImportAsyncTask importTask = SettingsHelper.this.importTask; + if (importTask != null && !importTask.importDone) { + finishImport(importListener, false, items); + } + SettingsHelper.this.importTask = this; + } + + @Override + protected List doInBackground(Void... voids) { + switch (importType) { + case COLLECT: + try { + return importer.collectItems(file); + } catch (IllegalArgumentException e) { + LOG.error("Failed to collect items from: " + file.getName(), e); + } catch (IOException e) { + LOG.error("Failed to collect items from: " + file.getName(), e); + } + break; + case CHECK_DUPLICATES: + this.duplicates = getDuplicatesData(selectedItems); + return selectedItems; + case IMPORT: + return items; + } + return null; + } + + @Override + protected void onPostExecute(@Nullable List items) { + if (items != null && importType != ImportType.CHECK_DUPLICATES) { + this.items = items; + } else { + selectedItems = items; + } + switch (importType) { + case COLLECT: + importDone = true; + collectListener.onSettingsCollectFinished(true, false, this.items); + break; + case CHECK_DUPLICATES: + importDone = true; + if (duplicatesListener != null) { + duplicatesListener.onDuplicatesChecked(duplicates, selectedItems); + } + break; + case IMPORT: + if (items != null && items.size() > 0) { + for (SettingsItem item : items) { + item.apply(); + } + new ImportItemsAsyncTask(file, importListener, items).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + break; + } + } + + public List getItems() { + return items; + } + + public File getFile() { + return file; + } + + public void setImportListener(SettingsImportListener importListener) { + this.importListener = importListener; + } + + public void setDuplicatesListener(CheckDuplicatesListener duplicatesListener) { + this.duplicatesListener = duplicatesListener; + } + + ImportType getImportType() { + return importType; + } + + boolean isImportDone() { + return importDone; + } + + public List getDuplicates() { + return duplicates; + } + + public List getSelectedItems() { + return selectedItems; + } + + private List getDuplicatesData(List items) { + List duplicateItems = new ArrayList<>(); + for (SettingsItem item : items) { + if (item instanceof ProfileSettingsItem) { + if (item.exists()) { + duplicateItems.add(((ProfileSettingsItem) item).getModeBean()); + } + } else if (item instanceof CollectionSettingsItem) { + List duplicates = ((CollectionSettingsItem) item).processDuplicateItems(); + if (!duplicates.isEmpty()) { + duplicateItems.addAll(duplicates); + } + } else if (item instanceof FileSettingsItem) { + if (item.exists()) { + duplicateItems.add(((FileSettingsItem) item).getFile()); + } + } + } + return duplicateItems; + } + } + + public void collectSettings(@NonNull File settingsFile, String latestChanges, int version, + @Nullable SettingsCollectListener listener) { + new ImportAsyncTask(settingsFile, latestChanges, version, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public void checkDuplicates(@NonNull File file, @NonNull List items, @NonNull List selectedItems, CheckDuplicatesListener listener) { + new ImportAsyncTask(file, items, selectedItems, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public void importSettings(@NonNull File settingsFile, @NonNull List items, String latestChanges, int version, @Nullable SettingsImportListener listener) { + new ImportAsyncTask(settingsFile, items, latestChanges, version, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public void exportSettings(@NonNull File fileDir, @NonNull String fileName, @Nullable SettingsExportListener listener, @NonNull List items, boolean exportItemsFiles) { + File file = new File(fileDir, fileName + OSMAND_SETTINGS_FILE_EXT); + ExportAsyncTask exportAsyncTask = new ExportAsyncTask(file, listener, items, exportItemsFiles); + exportAsyncTasks.put(file, exportAsyncTask); + exportAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + public void exportSettings(@NonNull File fileDir, @NonNull String fileName, @Nullable SettingsExportListener listener, + boolean exportItemsFiles, @NonNull SettingsItem... items) { + exportSettings(fileDir, fileName, listener, new ArrayList<>(Arrays.asList(items)), exportItemsFiles); + } + + public List getFilteredSettingsItems(Map> additionalData, + List settingsTypes) { + List settingsItems = new ArrayList<>(); + for (ExportSettingsType settingsType : settingsTypes) { + List settingsDataObjects = additionalData.get(settingsType); + if (settingsDataObjects != null) { + for (Object object : settingsDataObjects) { + if (object instanceof ApplicationModeBean) { + settingsItems.add(new ProfileSettingsItem(app, null, (ApplicationModeBean) object)); + } + } + settingsItems.addAll(prepareAdditionalSettingsItems(new ArrayList<>(settingsDataObjects))); + } + } + return settingsItems; + } + + public Map> getAdditionalData() { + Map> dataList = new HashMap<>(); + + QuickActionRegistry registry = app.getQuickActionRegistry(); + List actionsList = registry.getQuickActions(); + if (!actionsList.isEmpty()) { + dataList.put(ExportSettingsType.QUICK_ACTIONS, actionsList); + } + + List poiList = app.getPoiFilters().getUserDefinedPoiFilters(false); + if (!poiList.isEmpty()) { + dataList.put(ExportSettingsType.POI_TYPES, poiList); + } + + List iTileSources = new ArrayList<>(); + Set tileSourceNames = app.getSettings().getTileSourceEntries(true).keySet(); + for (String name : tileSourceNames) { + File f = app.getAppPath(IndexConstants.TILES_INDEX_DIR + name); + if (f != null) { + ITileSource template; + if (f.getName().endsWith(SQLiteTileSource.EXT)) { + template = new SQLiteTileSource(app, f, TileSourceManager.getKnownSourceTemplates()); + } else { + template = TileSourceManager.createTileSourceTemplate(f); + } + if (template.getUrlTemplate() != null) { + iTileSources.add(template); + } + } + } + if (!iTileSources.isEmpty()) { + dataList.put(ExportSettingsType.MAP_SOURCES, iTileSources); + } + + Map externalRenderers = app.getRendererRegistry().getExternalRenderers(); + if (!externalRenderers.isEmpty()) { + dataList.put(ExportSettingsType.CUSTOM_RENDER_STYLE, new ArrayList<>(externalRenderers.values())); + } + + File routingProfilesFolder = app.getAppPath(IndexConstants.ROUTING_PROFILES_DIR); + if (routingProfilesFolder.exists() && routingProfilesFolder.isDirectory()) { + File[] fl = routingProfilesFolder.listFiles(); + if (fl != null && fl.length > 0) { + dataList.put(ExportSettingsType.CUSTOM_ROUTING, Arrays.asList(fl)); + } + } + + Map impassableRoads = app.getAvoidSpecificRoads().getImpassableRoads(); + if (!impassableRoads.isEmpty()) { + dataList.put(ExportSettingsType.AVOID_ROADS, new ArrayList<>(impassableRoads.values())); + } + AudioVideoNotesPlugin plugin = OsmandPlugin.getPlugin(AudioVideoNotesPlugin.class); + if (plugin != null) { + List files = new ArrayList<>(); + for (Recording rec : plugin.getAllRecordings()) { + File file = rec.getFile(); + if (file != null && file.exists()) { + files.add(file); + } + } + if (!files.isEmpty()) { + dataList.put(ExportSettingsType.MULTIMEDIA_NOTES, files); + } + } + File gpxDir = app.getAppPath(IndexConstants.GPX_INDEX_DIR); + List gpxInfoList = GpxUiHelper.getSortedGPXFilesInfo(gpxDir, null, true); + if (!gpxInfoList.isEmpty()) { + List files = new ArrayList<>(); + for (GPXInfo gpxInfo : gpxInfoList) { + File file = new File(gpxInfo.getFileName()); + if (file.exists()) { + files.add(file); + } + } + if (!files.isEmpty()) { + dataList.put(ExportSettingsType.TRACKS, files); + } + } + return dataList; + } + + public List prepareAdditionalSettingsItems(List data) { + List settingsItems = new ArrayList<>(); + List quickActions = new ArrayList<>(); + List poiUIFilters = new ArrayList<>(); + List tileSourceTemplates = new ArrayList<>(); + List avoidRoads = new ArrayList<>(); + for (Object object : data) { + if (object instanceof QuickAction) { + quickActions.add((QuickAction) object); + } else if (object instanceof PoiUIFilter) { + poiUIFilters.add((PoiUIFilter) object); + } else if (object instanceof TileSourceTemplate || object instanceof SQLiteTileSource) { + tileSourceTemplates.add((ITileSource) object); + } else if (object instanceof File) { + try { + settingsItems.add(new FileSettingsItem(app, (File) object)); + } catch (IllegalArgumentException e) { + LOG.warn("Trying to export unsuported file type", e); + } + } else if (object instanceof AvoidRoadInfo) { + avoidRoads.add((AvoidRoadInfo) object); + } + } + if (!quickActions.isEmpty()) { + settingsItems.add(new QuickActionsSettingsItem(app, quickActions)); + } + if (!poiUIFilters.isEmpty()) { + settingsItems.add(new PoiUiFiltersSettingsItem(app, poiUIFilters)); + } + if (!tileSourceTemplates.isEmpty()) { + settingsItems.add(new MapSourcesSettingsItem(app, tileSourceTemplates)); + } + if (!avoidRoads.isEmpty()) { + settingsItems.add(new AvoidRoadsSettingsItem(app, avoidRoads)); + } + return settingsItems; + } + + public static Map> getSettingsToOperate(List settingsItems, boolean importComplete) { + Map> settingsToOperate = new HashMap<>(); + List profiles = new ArrayList<>(); + List quickActions = new ArrayList<>(); + List poiUIFilters = new ArrayList<>(); + List tileSourceTemplates = new ArrayList<>(); + List routingFilesList = new ArrayList<>(); + List renderFilesList = new ArrayList<>(); + List multimediaFilesList = new ArrayList<>(); + List tracksFilesList = new ArrayList<>(); + List avoidRoads = new ArrayList<>(); + for (SettingsItem item : settingsItems) { + switch (item.getType()) { + case PROFILE: + profiles.add(((ProfileSettingsItem) item).getModeBean()); + break; + case FILE: + FileSettingsItem fileItem = (FileSettingsItem) item; + if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.RENDERING_STYLE) { + renderFilesList.add(fileItem.getFile()); + } else if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.ROUTING_CONFIG) { + routingFilesList.add(fileItem.getFile()); + } else if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.MULTIMEDIA_NOTES) { + multimediaFilesList.add(fileItem.getFile()); + } else if (fileItem.getSubtype() == FileSettingsItem.FileSubtype.GPX) { + tracksFilesList.add(fileItem.getFile()); + } + break; + case QUICK_ACTIONS: + QuickActionsSettingsItem quickActionsItem = (QuickActionsSettingsItem) item; + if (importComplete) { + quickActions.addAll(quickActionsItem.getAppliedItems()); + } else { + quickActions.addAll(quickActionsItem.getItems()); + } + break; + case POI_UI_FILTERS: + PoiUiFiltersSettingsItem poiUiFilterItem = (PoiUiFiltersSettingsItem) item; + if (importComplete) { + poiUIFilters.addAll(poiUiFilterItem.getAppliedItems()); + } else { + poiUIFilters.addAll(poiUiFilterItem.getItems()); + } + break; + case MAP_SOURCES: + MapSourcesSettingsItem mapSourcesItem = (MapSourcesSettingsItem) item; + if (importComplete) { + tileSourceTemplates.addAll(mapSourcesItem.getAppliedItems()); + } else { + tileSourceTemplates.addAll(mapSourcesItem.getItems()); + } + break; + case AVOID_ROADS: + AvoidRoadsSettingsItem avoidRoadsItem = (AvoidRoadsSettingsItem) item; + if (importComplete) { + avoidRoads.addAll(avoidRoadsItem.getAppliedItems()); + } else { + avoidRoads.addAll(avoidRoadsItem.getItems()); + } + break; + default: + break; + } + } + + if (!profiles.isEmpty()) { + settingsToOperate.put(ExportSettingsType.PROFILE, profiles); + } + if (!quickActions.isEmpty()) { + settingsToOperate.put(ExportSettingsType.QUICK_ACTIONS, quickActions); + } + if (!poiUIFilters.isEmpty()) { + settingsToOperate.put(ExportSettingsType.POI_TYPES, poiUIFilters); + } + if (!tileSourceTemplates.isEmpty()) { + settingsToOperate.put(ExportSettingsType.MAP_SOURCES, tileSourceTemplates); + } + if (!renderFilesList.isEmpty()) { + settingsToOperate.put(ExportSettingsType.CUSTOM_RENDER_STYLE, renderFilesList); + } + if (!routingFilesList.isEmpty()) { + settingsToOperate.put(ExportSettingsType.CUSTOM_ROUTING, routingFilesList); + } + if (!avoidRoads.isEmpty()) { + settingsToOperate.put(ExportSettingsType.AVOID_ROADS, avoidRoads); + } + if (!multimediaFilesList.isEmpty()) { + settingsToOperate.put(ExportSettingsType.MULTIMEDIA_NOTES, multimediaFilesList); + } + if (!tracksFilesList.isEmpty()) { + settingsToOperate.put(ExportSettingsType.TRACKS, tracksFilesList); + } + return settingsToOperate; + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java new file mode 100644 index 0000000000..a06b8e8749 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java @@ -0,0 +1,137 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.util.Algorithms; + +import org.json.JSONException; + +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import static net.osmand.IndexConstants.OSMAND_SETTINGS_FILE_EXT; + +class SettingsImporter { + + private OsmandApplication app; + + SettingsImporter(@NonNull OsmandApplication app) { + this.app = app; + } + + List collectItems(@NonNull File file) throws IllegalArgumentException, IOException { + return processItems(file, null); + } + + void importItems(@NonNull File file, @NonNull List items) throws IllegalArgumentException, IOException { + processItems(file, items); + } + + private List getItemsFromJson(@NonNull File file) throws IOException { + List items = new ArrayList<>(); + ZipInputStream zis = new ZipInputStream(new FileInputStream(file)); + InputStream ois = new BufferedInputStream(zis); + try { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + String fileName = checkEntryName(entry.getName()); + if (fileName.equals("items.json")) { + String itemsJson = null; + try { + itemsJson = Algorithms.readFromInputStream(ois, false).toString(); + } catch (IOException e) { + SettingsHelper.LOG.error("Error reading items.json: " + itemsJson, e); + throw new IllegalArgumentException("No items"); + } finally { + zis.closeEntry(); + } + try { + SettingsItemsFactory itemsFactory = new SettingsItemsFactory(app, itemsJson); + items.addAll(itemsFactory.getItems()); + } catch (IllegalArgumentException e) { + SettingsHelper.LOG.error("Error parsing items: " + itemsJson, e); + throw new IllegalArgumentException("No items"); + } catch (JSONException e) { + SettingsHelper.LOG.error("Error parsing items: " + itemsJson, e); + throw new IllegalArgumentException("No items"); + } + break; + } + } + } catch (IOException ex) { + SettingsHelper.LOG.error("Failed to read next entry", ex); + } finally { + Algorithms.closeStream(ois); + Algorithms.closeStream(zis); + } + return items; + } + + private List processItems(@NonNull File file, @Nullable List items) throws IllegalArgumentException, IOException { + boolean collecting = items == null; + if (collecting) { + items = getItemsFromJson(file); + } else { + if (items.size() == 0) { + throw new IllegalArgumentException("No items"); + } + } + ZipInputStream zis = new ZipInputStream(new FileInputStream(file)); + InputStream ois = new BufferedInputStream(zis); + try { + ZipEntry entry; + while ((entry = zis.getNextEntry()) != null) { + String fileName = checkEntryName(entry.getName()); + SettingsItem item = null; + for (SettingsItem settingsItem : items) { + if (settingsItem != null && settingsItem.applyFileName(fileName)) { + item = settingsItem; + break; + } + } + if (item != null && collecting && item.shouldReadOnCollecting() + || item != null && !collecting && !item.shouldReadOnCollecting()) { + try { + SettingsItemReader reader = item.getReader(); + if (reader != null) { + reader.readFromStream(ois); + } + } catch (IllegalArgumentException e) { + item.warnings.add(app.getString(R.string.settings_item_read_error, item.getName())); + SettingsHelper.LOG.error("Error reading item data: " + item.getName(), e); + } catch (IOException e) { + item.warnings.add(app.getString(R.string.settings_item_read_error, item.getName())); + SettingsHelper.LOG.error("Error reading item data: " + item.getName(), e); + } finally { + zis.closeEntry(); + } + } + } + } catch (IOException ex) { + SettingsHelper.LOG.error("Failed to read next entry", ex); + } finally { + Algorithms.closeStream(ois); + Algorithms.closeStream(zis); + } + return items; + } + + private String checkEntryName(String entryName) { + String fileExt = OSMAND_SETTINGS_FILE_EXT + "/"; + int index = entryName.indexOf(fileExt); + if (index != -1) { + entryName = entryName.substring(index + fileExt.length()); + } + return entryName; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItem.java new file mode 100644 index 0000000000..caf2d57301 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItem.java @@ -0,0 +1,236 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; +import net.osmand.util.Algorithms; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +public abstract class SettingsItem { + + protected OsmandApplication app; + + protected String pluginId; + protected String fileName; + + boolean shouldReplace = false; + + protected List warnings; + + SettingsItem(@NonNull OsmandApplication app) { + this.app = app; + init(); + } + + SettingsItem(@NonNull OsmandApplication app, @Nullable SettingsItem baseItem) { + this.app = app; + if (baseItem != null) { + this.pluginId = baseItem.pluginId; + this.fileName = baseItem.fileName; + } + init(); + } + + SettingsItem(OsmandApplication app, @NonNull JSONObject json) throws JSONException { + this.app = app; + init(); + readFromJson(json); + } + + protected void init() { + warnings = new ArrayList<>(); + } + + public List getWarnings() { + return warnings; + } + + @NonNull + public abstract SettingsItemType getType(); + + @NonNull + public abstract String getName(); + + @NonNull + public abstract String getPublicName(@NonNull Context ctx); + + @NonNull + public String getDefaultFileName() { + return getName() + getDefaultFileExtension(); + } + + @NonNull + public String getDefaultFileExtension() { + return ".json"; + } + + public String getPluginId() { + return pluginId; + } + + @Nullable + public String getFileName() { + return fileName; + } + + public boolean applyFileName(@NonNull String fileName) { + String n = getFileName(); + return n != null && n.endsWith(fileName); + } + + public boolean shouldReadOnCollecting() { + return false; + } + + public void setShouldReplace(boolean shouldReplace) { + this.shouldReplace = shouldReplace; + } + + static SettingsItemType parseItemType(@NonNull JSONObject json) throws IllegalArgumentException, JSONException { + String type = json.has("type") ? json.getString("type") : null; + if (type == null) { + throw new IllegalArgumentException("No type field"); + } + if (type.equals("QUICK_ACTION")) { + type = "QUICK_ACTIONS"; + } + return SettingsItemType.valueOf(type); + } + + public boolean exists() { + return false; + } + + public void apply() { + // non implemented + } + + void readFromJson(@NonNull JSONObject json) throws JSONException { + pluginId = json.has("pluginId") ? json.getString("pluginId") : null; + if (json.has("name")) { + fileName = json.getString("name") + getDefaultFileExtension(); + } + if (json.has("file")) { + fileName = json.getString("file"); + } + readItemsFromJson(json); + } + + void writeToJson(@NonNull JSONObject json) throws JSONException { + json.put("type", getType().name()); + String pluginId = getPluginId(); + if (!Algorithms.isEmpty(pluginId)) { + json.put("pluginId", pluginId); + } + if (getWriter() != null) { + String fileName = getFileName(); + if (Algorithms.isEmpty(fileName)) { + fileName = getDefaultFileName(); + } + json.put("file", fileName); + } + writeItemsToJson(json); + } + + String toJson() throws JSONException { + JSONObject json = new JSONObject(); + writeToJson(json); + return json.toString(); + } + + void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { + // override + } + + void writeItemsToJson(@NonNull JSONObject json) { + // override + } + + @Nullable + abstract SettingsItemReader getReader(); + + @Nullable + abstract SettingsItemWriter getWriter(); + + @NonNull + SettingsItemReader getJsonReader() { + return new SettingsItemReader(this) { + @Override + public void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException { + StringBuilder buf = new StringBuilder(); + try { + BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + String str; + while ((str = in.readLine()) != null) { + buf.append(str); + } + } catch (IOException e) { + throw new IOException("Cannot read json body", e); + } + String json = buf.toString(); + if (json.length() == 0) { + throw new IllegalArgumentException("Json body is empty"); + } + try { + readItemsFromJson(new JSONObject(json)); + } catch (JSONException e) { + throw new IllegalArgumentException("Json parsing error", e); + } + } + }; + } + + @NonNull + SettingsItemWriter getJsonWriter() { + return new SettingsItemWriter(this) { + @Override + public boolean writeToStream(@NonNull OutputStream outputStream) throws IOException { + JSONObject json = new JSONObject(); + writeItemsToJson(json); + if (json.length() > 0) { + try { + String s = json.toString(2); + outputStream.write(s.getBytes("UTF-8")); + } catch (JSONException e) { + SettingsHelper.LOG.error("Failed to write json to stream", e); + } + return true; + } + return false; + } + }; + } + + @Override + public int hashCode() { + return (getType().name() + getName()).hashCode(); + } + + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + if (!(other instanceof SettingsItem)) { + return false; + } + + SettingsItem item = (SettingsItem) other; + return item.getType() == getType() + && item.getName().equals(getName()) + && Algorithms.stringsEqual(item.getFileName(), getFileName()); + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemReader.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemReader.java new file mode 100644 index 0000000000..daa66f979a --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemReader.java @@ -0,0 +1,17 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; + +import java.io.IOException; +import java.io.InputStream; + +public abstract class SettingsItemReader { + + private T item; + + public SettingsItemReader(@NonNull T item) { + this.item = item; + } + + public abstract void readFromStream(@NonNull InputStream inputStream) throws IOException, IllegalArgumentException; +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemType.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemType.java new file mode 100644 index 0000000000..1a25e24817 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemType.java @@ -0,0 +1,16 @@ +package net.osmand.plus.settings.backend.backup; + +public enum SettingsItemType { + GLOBAL, + PROFILE, + PLUGIN, + DATA, + FILE, + RESOURCES, + QUICK_ACTIONS, + POI_UI_FILTERS, + MAP_SOURCES, + AVOID_ROADS, + SUGGESTED_DOWNLOADS, + DOWNLOADS +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java new file mode 100644 index 0000000000..9e3cf61377 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java @@ -0,0 +1,21 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; + +import java.io.IOException; +import java.io.OutputStream; + +public abstract class SettingsItemWriter { + + private T item; + + public SettingsItemWriter(T item) { + this.item = item; + } + + public T getItem() { + return item; + } + + public abstract boolean writeToStream(@NonNull OutputStream outputStream) throws IOException; +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemsFactory.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemsFactory.java new file mode 100644 index 0000000000..d4a639da4d --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemsFactory.java @@ -0,0 +1,131 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class SettingsItemsFactory { + + private OsmandApplication app; + private List items = new ArrayList<>(); + + SettingsItemsFactory(@NonNull OsmandApplication app, String jsonStr) throws IllegalArgumentException, JSONException { + this.app = app; + collectItems(new JSONObject(jsonStr)); + } + + private void collectItems(JSONObject json) throws IllegalArgumentException, JSONException { + JSONArray itemsJson = json.getJSONArray("items"); + int version = json.has("version") ? json.getInt("version") : 1; + if (version > SettingsHelper.VERSION) { + throw new IllegalArgumentException("Unsupported osf version: " + version); + } + Map> pluginItems = new HashMap<>(); + for (int i = 0; i < itemsJson.length(); i++) { + JSONObject itemJson = itemsJson.getJSONObject(i); + SettingsItem item; + try { + item = createItem(itemJson); + items.add(item); + String pluginId = item.getPluginId(); + if (pluginId != null && item.getType() != SettingsItemType.PLUGIN) { + List items = pluginItems.get(pluginId); + if (items != null) { + items.add(item); + } else { + items = new ArrayList<>(); + items.add(item); + pluginItems.put(pluginId, items); + } + } + } catch (IllegalArgumentException e) { + SettingsHelper.LOG.error("Error creating item from json: " + itemJson, e); + } + } + if (items.size() == 0) { + throw new IllegalArgumentException("No items"); + } + for (SettingsItem item : items) { + if (item instanceof PluginSettingsItem) { + PluginSettingsItem pluginSettingsItem = ((PluginSettingsItem) item); + List pluginDependentItems = pluginItems.get(pluginSettingsItem.getName()); + if (!Algorithms.isEmpty(pluginDependentItems)) { + pluginSettingsItem.getPluginDependentItems().addAll(pluginDependentItems); + } + } + } + } + + @NonNull + public List getItems() { + return items; + } + + @Nullable + public SettingsItem getItemByFileName(@NonNull String fileName) { + for (SettingsItem item : items) { + if (Algorithms.stringsEqual(item.getFileName(), fileName)) { + return item; + } + } + return null; + } + + @NonNull + private SettingsItem createItem(@NonNull JSONObject json) throws IllegalArgumentException, JSONException { + SettingsItem item = null; + SettingsItemType type = SettingsItem.parseItemType(json); + OsmandSettings settings = app.getSettings(); + switch (type) { + case GLOBAL: + item = new GlobalSettingsItem(settings); + break; + case PROFILE: + item = new ProfileSettingsItem(app, json); + break; + case PLUGIN: + item = new PluginSettingsItem(app, json); + break; + case DATA: + item = new DataSettingsItem(app, json); + break; + case FILE: + item = new FileSettingsItem(app, json); + break; + case RESOURCES: + item = new ResourcesSettingsItem(app, json); + break; + case QUICK_ACTIONS: + item = new QuickActionsSettingsItem(app, json); + break; + case POI_UI_FILTERS: + item = new PoiUiFiltersSettingsItem(app, json); + break; + case MAP_SOURCES: + item = new MapSourcesSettingsItem(app, json); + break; + case AVOID_ROADS: + item = new AvoidRoadsSettingsItem(app, json); + break; + case SUGGESTED_DOWNLOADS: + item = new SuggestedDownloadsItem(app, json); + break; + case DOWNLOADS: + item = new DownloadsItem(app, json); + break; + } + return item; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItem.java new file mode 100644 index 0000000000..ae683f49b8 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItem.java @@ -0,0 +1,76 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.InputStream; + +public abstract class StreamSettingsItem extends SettingsItem { + + @Nullable + private InputStream inputStream; + protected String name; + + public StreamSettingsItem(@NonNull OsmandApplication app, @NonNull String name) { + super(app); + this.name = name; + this.fileName = name; + } + + StreamSettingsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + public StreamSettingsItem(@NonNull OsmandApplication app, @NonNull InputStream inputStream, @NonNull String name) { + super(app); + this.inputStream = inputStream; + this.name = name; + this.fileName = name; + } + + @Nullable + public InputStream getInputStream() { + return inputStream; + } + + protected void setInputStream(@Nullable InputStream inputStream) { + this.inputStream = inputStream; + } + + @NonNull + @Override + public String getName() { + return name; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return getName(); + } + + @NonNull + @Override + public String getDefaultFileExtension() { + return ""; + } + + @Override + void readFromJson(@NonNull JSONObject json) throws JSONException { + super.readFromJson(json); + name = json.has("name") ? json.getString("name") : null; + } + + @Nullable + @Override + public SettingsItemWriter getWriter() { + return new StreamSettingsItemWriter(this); + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItemReader.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItemReader.java new file mode 100644 index 0000000000..b231f1f8ae --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItemReader.java @@ -0,0 +1,10 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; + +public abstract class StreamSettingsItemReader extends SettingsItemReader { + + public StreamSettingsItemReader(@NonNull StreamSettingsItem item) { + super(item); + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItemWriter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItemWriter.java new file mode 100644 index 0000000000..0029568a93 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/StreamSettingsItemWriter.java @@ -0,0 +1,34 @@ +package net.osmand.plus.settings.backend.backup; + +import androidx.annotation.NonNull; + +import net.osmand.util.Algorithms; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class StreamSettingsItemWriter extends SettingsItemWriter { + + public StreamSettingsItemWriter(StreamSettingsItem item) { + super(item); + } + + @Override + public boolean writeToStream(@NonNull OutputStream outputStream) throws IOException { + boolean hasData = false; + InputStream is = getItem().getInputStream(); + if (is != null) { + byte[] data = new byte[SettingsHelper.BUFFER]; + int count; + while ((count = is.read(data, 0, SettingsHelper.BUFFER)) != -1) { + outputStream.write(data, 0, count); + if (!hasData) { + hasData = true; + } + } + Algorithms.closeStream(is); + } + return hasData; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SuggestedDownloadsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SuggestedDownloadsItem.java new file mode 100644 index 0000000000..d2dd9a8554 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SuggestedDownloadsItem.java @@ -0,0 +1,128 @@ +package net.osmand.plus.settings.backend.backup; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.CustomOsmandPlugin; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.util.Algorithms; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; + +public class SuggestedDownloadsItem extends SettingsItem { + + private List items; + + SuggestedDownloadsItem(@NonNull OsmandApplication app, @NonNull JSONObject json) throws JSONException { + super(app, json); + } + + @Override + protected void init() { + super.init(); + items = new ArrayList<>(); + } + + @NonNull + @Override + public SettingsItemType getType() { + return SettingsItemType.SUGGESTED_DOWNLOADS; + + } + + @NonNull + @Override + public String getName() { + return "suggested_downloads"; + } + + @NonNull + @Override + public String getPublicName(@NonNull Context ctx) { + return "suggested_downloads"; + } + + public List getItems() { + return items; + } + + @Override + void readItemsFromJson(@NonNull JSONObject json) throws IllegalArgumentException { + try { + if (!json.has("items")) { + return; + } + JSONArray jsonArray = json.getJSONArray("items"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject object = jsonArray.getJSONObject(i); + String scopeId = object.optString("scope-id"); + String searchType = object.optString("search-type"); + int limit = object.optInt("limit", -1); + + List names = new ArrayList<>(); + if (object.has("names")) { + JSONArray namesArray = object.getJSONArray("names"); + for (int j = 0; j < namesArray.length(); j++) { + names.add(namesArray.getString(j)); + } + } + CustomOsmandPlugin.SuggestedDownloadItem suggestedDownload = new CustomOsmandPlugin.SuggestedDownloadItem(scopeId, searchType, names, limit); + items.add(suggestedDownload); + } + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_read_error, String.valueOf(getType()))); + throw new IllegalArgumentException("Json parse error", e); + } + } + + @Override + void writeItemsToJson(@NonNull JSONObject json) { + JSONArray jsonArray = new JSONArray(); + if (!items.isEmpty()) { + try { + for (CustomOsmandPlugin.SuggestedDownloadItem downloadItem : items) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("scope-id", downloadItem.getScopeId()); + if (downloadItem.getLimit() != -1) { + jsonObject.put("limit", downloadItem.getLimit()); + } + if (!Algorithms.isEmpty(downloadItem.getSearchType())) { + jsonObject.put("search-type", downloadItem.getSearchType()); + } + if (!Algorithms.isEmpty(downloadItem.getNames())) { + JSONArray namesArray = new JSONArray(); + for (String downloadName : downloadItem.getNames()) { + namesArray.put(downloadName); + } + jsonObject.put("names", namesArray); + } + jsonArray.put(jsonObject); + } + json.put("items", jsonArray); + } catch (JSONException e) { + warnings.add(app.getString(R.string.settings_item_write_error, String.valueOf(getType()))); + SettingsHelper.LOG.error("Failed write to json", e); + } + } + } + + @Nullable + @Override + SettingsItemReader getReader() { + return null; + } + + @Nullable + @Override + SettingsItemWriter getWriter() { + return null; + } +} diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/OsmLoginDataBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/OsmLoginDataBottomSheet.java index 37e993cf43..2b7494f0a4 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/OsmLoginDataBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/OsmLoginDataBottomSheet.java @@ -12,13 +12,13 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; +import net.osmand.plus.osmedit.ValidateOsmLoginDetailsTask; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; -import net.osmand.plus.osmedit.SettingsOsmEditingActivity; import net.osmand.plus.settings.fragments.OnPreferenceChanged; public class OsmLoginDataBottomSheet extends BasePreferenceBottomSheet { @@ -84,7 +84,7 @@ public class OsmLoginDataBottomSheet extends BasePreferenceBottomSheet { app.getSettings().USER_NAME.set(userNameEditText.getText().toString()); app.getSettings().USER_PASSWORD.set(passwordEditText.getText().toString()); - new SettingsOsmEditingActivity.ValidateOsmLoginDetailsTask(app).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new ValidateOsmLoginDetailsTask(app).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); Fragment target = getTargetFragment(); Preference preference = getPreference(); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java index 86940e7d7a..db12e7d781 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java @@ -53,11 +53,7 @@ import com.google.android.material.snackbar.Snackbar; import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; import net.osmand.access.AccessibilitySettingsFragment; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandPreference; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; @@ -69,6 +65,10 @@ import net.osmand.plus.monitoring.MonitoringSettingsFragment; import net.osmand.plus.osmedit.OsmEditingFragment; import net.osmand.plus.profiles.SelectAppModesBottomSheetDialogFragment; import net.osmand.plus.profiles.SelectAppModesBottomSheetDialogFragment.AppModeChangedListener; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.settings.backend.CommonPreference; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.bottomsheets.BooleanPreferenceBottomSheet; import net.osmand.plus.settings.bottomsheets.ChangeGeneralProfilesPrefBottomSheet; import net.osmand.plus.settings.bottomsheets.EditTextPreferenceBottomSheet; @@ -112,7 +112,7 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl public enum SettingsScreenType { - MAIN_SETTINGS(MainSettingsFragment.TAG, false, null, R.xml.settings_main_screen, R.layout.global_preference_toolbar), + MAIN_SETTINGS(MainSettingsFragment.class.getName(), false, null, R.xml.settings_main_screen, R.layout.global_preference_toolbar), GLOBAL_SETTINGS(GlobalSettingsFragment.class.getName(), false, null, R.xml.global_settings, R.layout.global_preference_toolbar), CONFIGURE_PROFILE(ConfigureProfileFragment.class.getName(), true, null, R.xml.configure_profile, R.layout.profile_preference_toolbar_with_switch), PROXY_SETTINGS(ProxySettingsFragment.class.getName(), false, null, R.xml.proxy_preferences, R.layout.global_preferences_toolbar_with_switch), @@ -899,9 +899,13 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl } public static boolean showInstance(FragmentActivity activity, SettingsScreenType screenType, @Nullable ApplicationMode appMode) { + return showInstance(activity, screenType, null, new Bundle()); + } + + public static boolean showInstance(FragmentActivity activity, SettingsScreenType screenType, + @Nullable ApplicationMode appMode, @NonNull Bundle args) { try { Fragment fragment = Fragment.instantiate(activity, screenType.fragmentName); - Bundle args = new Bundle(); if (appMode != null) { args.putString(APP_MODE_KEY, appMode.getStringKey()); } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuRootFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuRootFragment.java index 330f1c0a18..beb1711f31 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuRootFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureMenuRootFragment.java @@ -1,7 +1,6 @@ package net.osmand.plus.settings.fragments; import android.app.Activity; -import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; @@ -22,13 +21,13 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import net.osmand.AndroidUtils; import net.osmand.PlatformUtil; -import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuItem; import net.osmand.plus.OsmandApplication; @@ -36,14 +35,14 @@ import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivityActions; -import net.osmand.plus.activities.PluginsActivity; +import net.osmand.plus.activities.PluginsFragment; import net.osmand.plus.base.BaseOsmAndFragment; import net.osmand.plus.dialogs.ConfigureMapMenu; import net.osmand.plus.helpers.FontCache; import net.osmand.plus.mapcontextmenu.MapContextMenu; +import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.widgets.style.CustomTypefaceSpan; - import org.apache.commons.logging.Log; import java.util.ArrayList; @@ -229,8 +228,7 @@ public class ConfigureMenuRootFragment extends BaseOsmAndFragment { if (holder instanceof DescriptionHolder) { DescriptionHolder descriptionHolder = (DescriptionHolder) holder; String plugins = getString(R.string.prefs_plugins); - setupClickableText( - descriptionHolder.description, (String) currentItem, plugins, new Intent(app, PluginsActivity.class)); + setupClickableText(descriptionHolder.description, (String) currentItem, plugins); descriptionHolder.image.setVisibility(View.GONE); } else { final ScreenType item = (ScreenType) currentItem; @@ -253,12 +251,15 @@ public class ConfigureMenuRootFragment extends BaseOsmAndFragment { return items.size(); } - private void setupClickableText(TextView textView, String text, String clickableText, final Intent intent) { + private void setupClickableText(TextView textView, String text, String clickableText) { SpannableString spannableString = new SpannableString(text); ClickableSpan clickableSpan = new ClickableSpan() { @Override public void onClick(@NonNull View view) { - startActivity(intent); + FragmentActivity activity = getActivity(); + if (activity != null) { + PluginsFragment.showInstance(activity.getSupportFragmentManager()); + } } }; try { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureProfileFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureProfileFragment.java index e65a351041..08aa65fd50 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureProfileFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ConfigureProfileFragment.java @@ -35,8 +35,8 @@ import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandPlugin; import net.osmand.plus.R; -import net.osmand.plus.settings.backend.SettingsHelper; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsCollectListener; +import net.osmand.plus.settings.backend.backup.SettingsHelper; +import net.osmand.plus.settings.backend.backup.SettingsHelper.SettingsCollectListener; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; @@ -44,6 +44,7 @@ import net.osmand.plus.helpers.FontCache; import net.osmand.plus.openseamapsplugin.NauticalMapsPlugin; import net.osmand.plus.profiles.SelectCopyAppModeBottomSheet; import net.osmand.plus.profiles.SelectCopyAppModeBottomSheet.CopyAppModePrefsListener; +import net.osmand.plus.settings.backend.backup.SettingsItem; import net.osmand.plus.settings.bottomsheets.ResetProfilePrefsBottomSheet; import net.osmand.plus.settings.bottomsheets.ResetProfilePrefsBottomSheet.ResetAppModePrefsListener; import net.osmand.plus.skimapsplugin.SkiMapsPlugin; @@ -186,9 +187,9 @@ public class ConfigureProfileFragment extends BaseSettingsFragment implements Co private void restoreCustomModeFromFile(final File file) { app.getSettingsHelper().collectSettings(file, "", 1, new SettingsCollectListener() { @Override - public void onSettingsCollectFinished(boolean succeed, boolean empty, @NonNull List items) { + public void onSettingsCollectFinished(boolean succeed, boolean empty, @NonNull List items) { if (succeed) { - for (SettingsHelper.SettingsItem item : items) { + for (SettingsItem item : items) { item.setShouldReplace(true); } importBackupSettingsItems(file, items); @@ -197,10 +198,10 @@ public class ConfigureProfileFragment extends BaseSettingsFragment implements Co }); } - private void importBackupSettingsItems(File file, List items) { + private void importBackupSettingsItems(File file, List items) { app.getSettingsHelper().importSettings(file, items, "", 1, new SettingsHelper.SettingsImportListener() { @Override - public void onSettingsImportFinished(boolean succeed, @NonNull List items) { + public void onSettingsImportFinished(boolean succeed, @NonNull List items) { app.showToastMessage(R.string.profile_prefs_reset_successful); updateCopiedOrResetPrefs(); } @@ -368,7 +369,7 @@ public class ConfigureProfileFragment extends BaseSettingsFragment implements Co } List plugins = OsmandPlugin.getVisiblePlugins(); for (OsmandPlugin plugin : plugins) { - if (plugin instanceof SkiMapsPlugin || plugin instanceof NauticalMapsPlugin || plugin.getSettingsFragment() == null) { + if (plugin instanceof SkiMapsPlugin || plugin instanceof NauticalMapsPlugin || plugin.getSettingsScreenType() == null) { continue; } Preference preference = new Preference(ctx); @@ -378,7 +379,7 @@ public class ConfigureProfileFragment extends BaseSettingsFragment implements Co preference.setSummary(plugin.getPrefsDescription()); preference.setIcon(getContentIcon(plugin.getLogoResourceId())); preference.setLayoutResource(R.layout.preference_with_descr); - preference.setFragment(plugin.getSettingsFragment().getName()); + preference.setFragment(plugin.getSettingsScreenType().fragmentName); preferenceCategory.addPreference(preference); } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/DuplicatesSettingsAdapter.java b/OsmAnd/src/net/osmand/plus/settings/fragments/DuplicatesSettingsAdapter.java index dc78f5755c..8160e81e6e 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/DuplicatesSettingsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/DuplicatesSettingsAdapter.java @@ -13,6 +13,8 @@ import net.osmand.AndroidUtils; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.map.ITileSource; +import net.osmand.plus.audionotes.AudioVideoNotesPlugin; +import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; import net.osmand.plus.OsmandApplication; @@ -131,6 +133,15 @@ public class DuplicatesSettingsAdapter extends RecyclerView.Adapter tileSources = new ArrayList<>(); List renderFilesList = new ArrayList<>(); List routingFilesList = new ArrayList<>(); + List multimediaFilesList = new ArrayList<>(); + List trackFilesList = new ArrayList<>(); List avoidRoads = new ArrayList<>(); for (Object object : duplicatesList) { @@ -184,10 +190,14 @@ public class ImportDuplicatesFragment extends BaseOsmAndFragment implements View tileSources.add((ITileSource) object); } else if (object instanceof File) { File file = (File) object; - if (file.getAbsolutePath().contains("files/rendering")) { + if (file.getAbsolutePath().contains(RENDERERS_DIR)) { renderFilesList.add(file); - } else if (file.getAbsolutePath().contains("files/routing")) { + } else if (file.getAbsolutePath().contains(ROUTING_PROFILES_DIR)) { routingFilesList.add(file); + } else if (file.getAbsolutePath().contains(AV_INDEX_DIR)) { + multimediaFilesList.add(file); + } else if (file.getAbsolutePath().contains(GPX_INDEX_DIR)) { + trackFilesList.add(file); } } else if (object instanceof AvoidRoadInfo) { avoidRoads.add((AvoidRoadInfo) object); @@ -217,6 +227,14 @@ public class ImportDuplicatesFragment extends BaseOsmAndFragment implements View duplicates.add(getString(R.string.shared_string_rendering_style)); duplicates.addAll(renderFilesList); } + if (!multimediaFilesList.isEmpty()) { + duplicates.add(getString(R.string.audionotes_plugin_name)); + duplicates.addAll(multimediaFilesList); + } + if (!trackFilesList.isEmpty()) { + duplicates.add(getString(R.string.shared_string_tracks)); + duplicates.addAll(trackFilesList); + } if (!avoidRoads.isEmpty()) { duplicates.add(getString(R.string.avoid_road)); duplicates.addAll(avoidRoads); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java index dd02f3f716..6002c8a58e 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java @@ -42,17 +42,17 @@ import net.osmand.plus.poi.PoiUIFilter; import net.osmand.plus.quickaction.QuickAction; import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; import net.osmand.plus.settings.backend.ExportSettingsType; -import net.osmand.plus.settings.backend.SettingsHelper; -import net.osmand.plus.settings.backend.SettingsHelper.AvoidRoadsSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.FileSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.ImportAsyncTask; -import net.osmand.plus.settings.backend.SettingsHelper.ImportType; -import net.osmand.plus.settings.backend.SettingsHelper.MapSourcesSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.PoiUiFiltersSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.ProfileSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.QuickActionsSettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsItemType; +import net.osmand.plus.settings.backend.backup.SettingsHelper; +import net.osmand.plus.settings.backend.backup.AvoidRoadsSettingsItem; +import net.osmand.plus.settings.backend.backup.FileSettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsHelper.ImportAsyncTask; +import net.osmand.plus.settings.backend.backup.SettingsHelper.ImportType; +import net.osmand.plus.settings.backend.backup.MapSourcesSettingsItem; +import net.osmand.plus.settings.backend.backup.PoiUiFiltersSettingsItem; +import net.osmand.plus.settings.backend.backup.ProfileSettingsItem; +import net.osmand.plus.settings.backend.backup.QuickActionsSettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsItemType; import net.osmand.plus.widgets.TextViewEx; import net.osmand.util.Algorithms; diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportedSettingsItemsAdapter.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportedSettingsItemsAdapter.java index e663e6f189..57198c2f04 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportedSettingsItemsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportedSettingsItemsAdapter.java @@ -106,6 +106,14 @@ public class ImportedSettingsItemsAdapter extends holder.icon.setImageDrawable(uiUtils.getIcon(R.drawable.ic_action_alert, activeColorRes)); holder.title.setText(R.string.avoid_road); break; + case MULTIMEDIA_NOTES: + holder.icon.setImageDrawable(uiUtils.getIcon(R.drawable.ic_action_photo_dark, activeColorRes)); + holder.title.setText(R.string.audionotes_plugin_name); + break; + case TRACKS: + holder.icon.setImageDrawable(uiUtils.getIcon(R.drawable.ic_action_route_distance, activeColorRes)); + holder.title.setText(R.string.shared_string_tracks); + break; } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java index c3381520a7..51a6f43531 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/MainSettingsFragment.java @@ -18,8 +18,8 @@ import net.osmand.CallbackWithObject; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsItem; -import net.osmand.plus.settings.backend.SettingsHelper.SettingsItemType; +import net.osmand.plus.settings.backend.backup.SettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsItemType; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java index 6fcf8a17bf..1bdbc38b19 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ProfileAppearanceFragment.java @@ -42,7 +42,8 @@ import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.R; -import net.osmand.plus.settings.backend.SettingsHelper; +import net.osmand.plus.settings.backend.backup.ProfileSettingsItem; +import net.osmand.plus.settings.backend.backup.SettingsHelper; import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities.DialogButtonType; import net.osmand.plus.profiles.LocationIcon; @@ -828,7 +829,7 @@ public class ProfileAppearanceFragment extends BaseSettingsFragment { tempDir.mkdirs(); } app.getSettingsHelper().exportSettings(tempDir, mode.getStringKey(), - getSettingsExportListener(), true, new SettingsHelper.ProfileSettingsItem(app, mode)); + getSettingsExportListener(), true, new ProfileSettingsItem(app, mode)); } }