Merge pull request #10198 from osmandapp/export_ui

Export UI first screen
This commit is contained in:
alex-osm 2020-11-17 15:10:15 +03:00 committed by GitHub
commit 3f1458579e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1333 additions and 662 deletions

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/warning_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/bg_color"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/warning_img"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginTop="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:src="@drawable/ic_action_sdcard_warning_colored" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginTop="@dimen/measurement_tool_menu_title_padding_top"
android:layout_marginRight="@dimen/content_padding"
android:layout_marginBottom="@dimen/measurement_tool_menu_title_padding_top"
android:orientation="vertical">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/warning_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:text="@string/export_not_enough_space"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size"
app:lineHeight="@dimen/default_title_line_height"
app:typeface="@string/font_roboto_medium" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/warning_descr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:letterSpacing="@dimen/description_letter_spacing"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_desc_text_size"
app:lineHeight="@dimen/default_desc_line_height"
app:typeface="@string/font_roboto_regular"
tools:text="@string/export_not_enough_space_descr" />
</LinearLayout>
</LinearLayout>
<include layout="@layout/card_bottom_divider" />
</LinearLayout>

View file

@ -21,73 +21,80 @@
<LinearLayout
android:id="@+id/buttons_container"
android:layout_width="match_parent"
android:layout_height="@dimen/dialog_button_ex_height"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?attr/bg_color"
android:gravity="center"
android:minHeight="@dimen/bottom_sheet_title_height"
android:orientation="vertical">
<include layout="@layout/divider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/content_padding_small">
android:layout_height="wrap_content"
android:orientation="horizontal">
<FrameLayout
<LinearLayout
android:id="@+id/file_size_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1">
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:paddingLeft="@dimen/content_padding"
android:paddingTop="@dimen/bottom_sheet_title_padding_bottom"
android:paddingRight="@dimen/content_padding"
android:paddingBottom="@dimen/bottom_sheet_title_padding_bottom"
android:visibility="invisible"
tools:visibility="visible">
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/select_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:id="@+id/file_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:letterSpacing="@dimen/description_letter_spacing"
android:maxLines="1"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_desc_text_size"
osmand:lineHeight="@dimen/default_desc_line_height"
osmand:typeface="@string/font_roboto_regular"
tools:text="567 MB" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/file_size_descr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="start|center_vertical"
android:letterSpacing="@dimen/description_letter_spacing"
android:maxLines="1"
android:paddingLeft="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding_small"
android:text="@string/shared_string_select_all"
android:textColor="?attr/active_color_basic"
android:text="@string/approximate_file_size"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="Select all" />
osmand:lineHeight="@dimen/default_desc_line_height"
osmand:typeface="@string/font_roboto_regular" />
</FrameLayout>
</LinearLayout>
<View
android:layout_width="@dimen/content_padding"
android:layout_height="match_parent" />
<FrameLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/dlg_btn_primary">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding_half"
android:layout_marginTop="@dimen/content_padding_half"
android:layout_marginRight="@dimen/content_padding_half"
android:layout_marginBottom="@dimen/content_padding_half">
<net.osmand.plus.widgets.TextViewEx
<include
android:id="@+id/continue_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackground"
android:ellipsize="end"
android:gravity="center"
android:maxLines="1"
android:paddingLeft="@dimen/content_padding_small"
android:paddingRight="@dimen/content_padding_small"
android:text="@string/shared_string_continue"
android:textColor="?attr/dlg_btn_primary_text"
android:textSize="@dimen/default_desc_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="Continue" />
layout="@layout/bottom_sheet_dialog_button"
android:visibility="visible" />
</FrameLayout>
</LinearLayout>
</LinearLayout>
@ -95,8 +102,8 @@
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/transparent">
<com.google.android.material.appbar.CollapsingToolbarLayout
@ -104,24 +111,24 @@
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height_expanded"
android:background="?attr/actionModeBackground"
osmand:collapsedTitleTextAppearance="@style/AppBarTitle"
osmand:expandedTitleGravity="start|bottom"
osmand:expandedTitleMarginBottom="@dimen/content_padding_small"
osmand:expandedTitleMarginEnd="@dimen/content_padding"
osmand:expandedTitleMarginStart="@dimen/content_padding"
osmand:collapsedTitleTextAppearance="@style/AppBarTitle"
osmand:expandedTitleGravity="start|bottom"
osmand:expandedTitleTextAppearance="@style/AppBarTitle"
osmand:layout_scrollFlags="scroll|exitUntilCollapsed">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
osmand:titleMarginEnd="0dp"
osmand:titleMarginStart="0dp"
android:layout_width="match_parent"
android:layout_height="@dimen/toolbar_height"
android:minHeight="@dimen/toolbar_height"
osmand:layout_collapseMode="pin"
osmand:layout_scrollFlags="scroll|enterAlways|exitUntilCollapsed"
osmand:title="@string/shared_string_import">
osmand:title="@string/shared_string_import"
osmand:titleMarginEnd="0dp"
osmand:titleMarginStart="0dp">
</androidx.appcompat.widget.Toolbar>
@ -132,8 +139,8 @@
style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="5dp"
android:visibility="gone"
android:indeterminate="true"
android:visibility="gone"
osmand:mpb_progressStyle="horizontal"
osmand:mpb_setBothDrawables="true"
osmand:mpb_useIntrinsicPadding="false"

View file

@ -10,15 +10,25 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/bg_color"
android:lineSpacingMultiplier="@dimen/line_spacing_multiplier_description"
android:paddingStart="@dimen/content_padding"
android:paddingTop="@dimen/list_header_settings_top_margin"
android:paddingEnd="@dimen/content_padding"
android:lineSpacingMultiplier="@dimen/line_spacing_multiplier_description"
android:paddingBottom="@dimen/list_header_settings_top_margin"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size"
tools:text="@string/select_data_to_import" />
<include layout="@layout/card_bottom_divider" />
<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/list_divider"
android:visibility="gone"
tools:visibility="visible" />
<include
android:id="@+id/card_bottom_divider"
layout="@layout/card_bottom_divider" />
</LinearLayout>

View file

@ -14,32 +14,32 @@
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="?attr/bg_color">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="66dp"
android:minHeight="@dimen/setting_list_item_large_height"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/explist_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/content_padding"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginTop="@dimen/content_padding"
android:layout_marginEnd="@dimen/list_content_padding_large"
android:layout_marginRight="@dimen/list_content_padding_large"
android:layout_marginBottom="@dimen/content_padding"
android:layout_marginTop="@dimen/bottom_sheet_icon_margin"
android:layout_marginRight="@dimen/content_padding"
android:layout_marginBottom="@dimen/bottom_sheet_icon_margin"
osmand:srcCompat="@drawable/ic_action_arrow_down" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical">
@ -48,16 +48,20 @@
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/default_list_text_size"
osmand:lineHeight="@dimen/default_title_line_height"
tools:text="Quick actions" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/sub_text_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:letterSpacing="@dimen/description_letter_spacing"
android:textColor="?android:textColorSecondary"
android:textSize="@dimen/default_desc_text_size"
osmand:lineHeight="@dimen/default_desc_line_height"
tools:text="8 of 4" />
</LinearLayout>
@ -66,22 +70,22 @@
android:id="@+id/vertical_divider"
android:layout_width="1dp"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/content_padding"
android:layout_marginBottom="@dimen/content_padding"
android:layout_marginTop="@dimen/setting_profile_item_switch_margin"
android:layout_marginBottom="@dimen/setting_profile_item_switch_margin"
android:background="?attr/list_divider" />
<FrameLayout
android:id="@+id/check_box_container"
style="@style/Widget.AppCompat.Toolbar.Button.Navigation"
android:layout_width="@dimen/acceptable_touch_radius"
android:layout_height="@dimen/acceptable_touch_radius"
android:layout_marginEnd="@dimen/text_margin_small"
android:layout_marginRight="@dimen/text_margin_small"
android:background="?attr/selectableItemBackgroundBorderless">
android:layout_marginRight="@dimen/text_margin_small">
<net.osmand.view.ThreeStateCheckbox
android:id="@+id/check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_gravity="center"
android:clickable="false"
android:enabled="false"

View file

@ -210,6 +210,8 @@
<dimen name="welcome_header_text_size">23sp</dimen>
<dimen name="text_button_text_size">15sp</dimen>
<dimen name="travel_card_primary_text_size">15sp</dimen>
<dimen name="default_title_line_height">24sp</dimen>
<dimen name="default_desc_line_height">20sp</dimen>
<dimen name="default_split_segments_overview">13sp</dimen>
<dimen name="default_split_segments_data">13sp</dimen>

View file

@ -11,6 +11,12 @@
Thx - Hardy
-->
<string name="export_not_enough_space">There is not enough space</string>
<string name="export_not_enough_space_descr">Your device only has %1$s free. Please free up some space or unselect some items to export.</string>
<string name="file_size_needed_for_import">Needed for import</string>
<string name="select_data_to_export">Select the data to be exported to the file.</string>
<string name="approximate_file_size">Approximate file size</string>
<string name="shared_string_resources">Resources</string>
<string name="app_mode_motorboat">Motorboat</string>
<string name="app_mode_kayak">Kayak</string>
<string name="shared_string_search_history">Search history</string>

View file

@ -788,6 +788,14 @@ public class AndroidUtils {
return result;
}
public static long getAvailableSpace(@Nullable File dir) {
if (dir != null && dir.canRead()) {
StatFs fs = new StatFs(dir.getAbsolutePath());
return fs.getAvailableBlocksLong() * fs.getBlockSize();
}
return -1;
}
public static float getFreeSpaceGb(File dir) {
if (dir.canRead()) {
StatFs fs = new StatFs(dir.getAbsolutePath());

View file

@ -2312,7 +2312,7 @@ public class OsmandAidlApi {
File exportDir = app.getSettings().getExternalStorageDirectory();
String fileName = appMode.toHumanString();
SettingsHelper settingsHelper = app.getSettingsHelper();
settingsItems.addAll(settingsHelper.getFilteredSettingsItems(settingsHelper.getAdditionalData(false), settingsTypes));
settingsItems.addAll(settingsHelper.getFilteredSettingsItems(settingsTypes, false));
settingsHelper.exportSettings(exportDir, fileName, null, settingsItems, true);
return true;
}

View file

@ -22,7 +22,6 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.Space;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -36,7 +35,6 @@ import androidx.fragment.app.FragmentActivity;
import androidx.viewpager.widget.ViewPager;
import net.osmand.AndroidUtils;
import net.osmand.IProgress;
import net.osmand.IndexConstants;
import net.osmand.PlatformUtil;
import net.osmand.access.AccessibilityAssistant;
@ -45,7 +43,6 @@ import net.osmand.data.PointDescription;
import net.osmand.map.WorldRegion;
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.Version;
import net.osmand.plus.activities.LocalIndexInfo;
@ -64,6 +61,8 @@ import net.osmand.plus.helpers.FileNameTranslationHelper;
import net.osmand.plus.inapp.InAppPurchaseHelper;
import net.osmand.plus.inapp.InAppPurchaseHelper.InAppPurchaseTaskType;
import net.osmand.plus.openseamapsplugin.NauticalMapsPlugin;
import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.download.ReloadIndexesTask.ReloadIndexesListener;
import net.osmand.plus.srtmplugin.SRTMPlugin;
import net.osmand.plus.views.controls.PagerSlidingTabStrip;
import net.osmand.util.Algorithms;
@ -390,7 +389,6 @@ public class DownloadActivity extends AbstractDownloadActivity implements Downlo
|| application.getSettings().SHOULD_SHOW_FREE_VERSION_BANNER.get();
}
public static class FreeVersionBanner {
private final View freeVersionBanner;
private final View freeVersionBannerTitle;
@ -441,7 +439,7 @@ public class DownloadActivity extends AbstractDownloadActivity implements Downlo
freeVersionBanner.setVisibility(View.VISIBLE);
downloadsLeftProgressBar.setMax(DownloadValidationManager.MAXIMUM_AVAILABLE_FREE_DOWNLOADS);
freeVersionDescriptionTextView.setText(ctx.getString(R.string.free_version_message,
DownloadValidationManager.MAXIMUM_AVAILABLE_FREE_DOWNLOADS +"" ));
DownloadValidationManager.MAXIMUM_AVAILABLE_FREE_DOWNLOADS + ""));
LinearLayout marksLinearLayout = (LinearLayout) freeVersionBanner.findViewById(R.id.marksLinearLayout);
Space spaceView = new Space(ctx);
@ -493,6 +491,7 @@ public class DownloadActivity extends AbstractDownloadActivity implements Downlo
freeVersionBannerTitle.setVisibility(View.VISIBLE);
}
}
private void updateAvailableDownloads() {
int activeTasks = ctx.getDownloadThread().getCountedDownloads();
OsmandSettings settings = ctx.getMyApplication().getSettings();
@ -570,36 +569,26 @@ public class DownloadActivity extends AbstractDownloadActivity implements Downlo
}
}
@SuppressLint("StaticFieldLeak")
public void reloadLocalIndexes() {
AsyncTask<Void, String, List<String>> task = new AsyncTask<Void, String, List<String>>() {
final OsmandApplication app = (OsmandApplication) getApplication();
ReloadIndexesTask reloadIndexesTask = new ReloadIndexesTask(app, new ReloadIndexesListener() {
@Override
protected void onPreExecute() {
super.onPreExecute();
public void reloadIndexesStarted() {
setSupportProgressBarIndeterminateVisibility(true);
}
@Override
protected List<String> doInBackground(Void... params) {
return getMyApplication().getResourceManager().reloadIndexes(IProgress.EMPTY_PROGRESS,
new ArrayList<String>()
);
}
@Override
protected void onPostExecute(List<String> warnings) {
public void reloadIndexesFinished(List<String> warnings) {
setSupportProgressBarIndeterminateVisibility(false);
if (!warnings.isEmpty()) {
Toast.makeText(DownloadActivity.this, AndroidUtils.formatWarnings(warnings).toString(), Toast.LENGTH_LONG).show();
if (!Algorithms.isEmpty(warnings)) {
app.showToastMessage(AndroidUtils.formatWarnings(warnings).toString());
}
newDownloadIndexes();
}
};
task.executeOnExecutor(singleThreadExecutor);
});
reloadIndexesTask.executeOnExecutor(singleThreadExecutor);
}
public void setDownloadItem(WorldRegion region, String targetFileName) {
if (downloadItem == null) {
downloadItem = region;
@ -666,8 +655,8 @@ public class DownloadActivity extends AbstractDownloadActivity implements Downlo
int percent = 0;
if (dir.canRead()) {
StatFs fs = new StatFs(dir.getAbsolutePath());
size = AndroidUtils.formatSize(activity, ((long)fs.getAvailableBlocks()) * fs.getBlockSize());
percent = 100 - (int)((long)fs.getAvailableBlocks() * 100 / fs.getBlockCount());
size = AndroidUtils.formatSize(activity, ((long) fs.getAvailableBlocks()) * fs.getBlockSize());
percent = 100 - (int) ((long) fs.getAvailableBlocks() * 100 / fs.getBlockCount());
}
sizeProgress.setIndeterminate(false);
sizeProgress.setProgress(percent);

View file

@ -0,0 +1,49 @@
package net.osmand.plus.download;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.IProgress;
import net.osmand.plus.OsmandApplication;
import java.util.ArrayList;
import java.util.List;
public class ReloadIndexesTask extends AsyncTask<Void, String, List<String>> {
private final OsmandApplication app;
private final ReloadIndexesListener listener;
public ReloadIndexesTask(@NonNull OsmandApplication app, @Nullable ReloadIndexesListener listener) {
this.app = app;
this.listener = listener;
}
@Override
protected void onPreExecute() {
if (listener != null) {
listener.reloadIndexesStarted();
}
}
@Override
protected List<String> doInBackground(Void... params) {
return app.getResourceManager().reloadIndexes(IProgress.EMPTY_PROGRESS, new ArrayList<String>());
}
@Override
protected void onPostExecute(List<String> warnings) {
if (listener != null) {
listener.reloadIndexesFinished(warnings);
}
}
public interface ReloadIndexesListener {
void reloadIndexesStarted();
void reloadIndexesFinished(List<String> warnings);
}
}

View file

@ -0,0 +1,22 @@
package net.osmand.plus.settings.backend;
import androidx.annotation.StringRes;
import net.osmand.plus.R;
public enum ExportSettingsCategory {
SETTINGS(R.string.shared_string_settings),
MY_PLACES(R.string.shared_string_my_places),
RESOURCES(R.string.shared_string_resources);
@StringRes
private final int titleId;
ExportSettingsCategory(@StringRes int titleId) {
this.titleId = titleId;
}
public int getTitleId() {
return titleId;
}
}

View file

@ -1,23 +1,63 @@
package net.osmand.plus.settings.backend;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import net.osmand.plus.R;
public enum ExportSettingsType {
PROFILE,
QUICK_ACTIONS,
POI_TYPES,
MAP_SOURCES,
CUSTOM_RENDER_STYLE,
CUSTOM_ROUTING,
AVOID_ROADS,
TRACKS,
MULTIMEDIA_NOTES,
GLOBAL,
OSM_NOTES,
OSM_EDITS,
OFFLINE_MAPS,
FAVORITES,
TTS_VOICE,
VOICE,
ACTIVE_MARKERS,
HISTORY_MARKERS,
SEARCH_HISTORY
PROFILE(R.string.shared_string_profiles, R.drawable.ic_action_manage_profiles),
GLOBAL(R.string.general_settings_2, R.drawable.ic_action_settings),
QUICK_ACTIONS(R.string.configure_screen_quick_action, R.drawable.ic_quick_action),
POI_TYPES(R.string.poi_dialog_poi_type, R.drawable.ic_action_info_dark),
SEARCH_HISTORY(R.string.shared_string_search_history, R.drawable.ic_action_history),
AVOID_ROADS(R.string.avoid_road, R.drawable.ic_action_alert),
FAVORITES(R.string.shared_string_favorites, R.drawable.ic_action_favorite),
TRACKS(R.string.shared_string_tracks, R.drawable.ic_action_route_distance),
OSM_NOTES(R.string.osm_notes, R.drawable.ic_action_openstreetmap_logo),
OSM_EDITS(R.string.osm_edits, R.drawable.ic_action_openstreetmap_logo),
MULTIMEDIA_NOTES(R.string.audionotes_plugin_name, R.drawable.ic_grouped_by_type),
ACTIVE_MARKERS(R.string.map_markers, R.drawable.ic_action_flag),
HISTORY_MARKERS(R.string.markers_history, R.drawable.ic_action_flag),
CUSTOM_RENDER_STYLE(R.string.shared_string_rendering_style, R.drawable.ic_action_map_style),
CUSTOM_ROUTING(R.string.shared_string_routing, R.drawable.ic_action_route_distance),
MAP_SOURCES(R.string.quick_action_map_source_title, R.drawable.ic_map),
OFFLINE_MAPS(R.string.shared_string_maps, R.drawable.ic_map),
TTS_VOICE(R.string.local_indexes_cat_tts, R.drawable.ic_action_volume_up),
VOICE(R.string.local_indexes_cat_voice, R.drawable.ic_action_volume_up);
@StringRes
private final int titleId;
@DrawableRes
private final int drawableRes;
ExportSettingsType(@StringRes int titleId, @DrawableRes int drawableRes) {
this.titleId = titleId;
this.drawableRes = drawableRes;
}
@StringRes
public int getTitleId() {
return titleId;
}
@DrawableRes
public int getIconRes() {
return drawableRes;
}
public boolean isSettingsCategory() {
return this == PROFILE || this == GLOBAL || this == QUICK_ACTIONS || this == POI_TYPES
|| this == SEARCH_HISTORY || this == AVOID_ROADS;
}
public boolean isMyPlacesCategory() {
return this == FAVORITES || this == TRACKS || this == OSM_EDITS || this == OSM_NOTES
|| this == MULTIMEDIA_NOTES || this == ACTIVE_MARKERS || this == HISTORY_MARKERS;
}
public boolean isResourcesCategory() {
return this == CUSTOM_RENDER_STYLE || this == CUSTOM_ROUTING || this == MAP_SOURCES
|| this == OFFLINE_MAPS || this == VOICE || this == TTS_VOICE;
}
}

View file

@ -29,10 +29,10 @@ import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo;
import net.osmand.plus.helpers.FileNameTranslationHelper;
import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.helpers.GpxUiHelper.GPXInfo;
import net.osmand.plus.mapmarkers.MapMarker;
import net.osmand.plus.mapmarkers.MapMarkersGroup;
import net.osmand.plus.helpers.SearchHistoryHelper;
import net.osmand.plus.helpers.SearchHistoryHelper.HistoryEntry;
import net.osmand.plus.mapmarkers.MapMarker;
import net.osmand.plus.mapmarkers.MapMarkersGroup;
import net.osmand.plus.osmedit.OpenstreetmapPoint;
import net.osmand.plus.osmedit.OsmEditingPlugin;
import net.osmand.plus.osmedit.OsmNotesPoint;
@ -41,7 +41,9 @@ import net.osmand.plus.quickaction.QuickAction;
import net.osmand.plus.quickaction.QuickActionRegistry;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean;
import net.osmand.plus.settings.backend.ExportSettingsCategory;
import net.osmand.plus.settings.backend.ExportSettingsType;
import net.osmand.plus.settings.fragments.ExportDataObject;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
@ -54,6 +56,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -467,6 +470,17 @@ public class SettingsHelper {
exportSettings(fileDir, fileName, listener, new ArrayList<>(Arrays.asList(items)), exportItemsFiles);
}
public List<SettingsItem> getFilteredSettingsItems(List<ExportSettingsType> settingsTypes, boolean globalExport) {
Map<ExportSettingsCategory, List<ExportDataObject>> dataList = getAdditionalData(globalExport);
Map<ExportSettingsType, List<?>> typesMap = new HashMap<>();
for (List<ExportDataObject> objects : dataList.values()) {
for (ExportDataObject exportObject : objects) {
typesMap.put(exportObject.getType(), exportObject.getItems());
}
}
return getFilteredSettingsItems(typesMap, settingsTypes);
}
public List<SettingsItem> getFilteredSettingsItems(Map<ExportSettingsType, List<?>> additionalData,
List<ExportSettingsType> settingsTypes) {
List<SettingsItem> settingsItems = new ArrayList<>();
@ -484,20 +498,139 @@ public class SettingsHelper {
return settingsItems;
}
public Map<ExportSettingsType, List<?>> getAdditionalData(boolean globalExport) {
Map<ExportSettingsType, List<?>> dataList = new HashMap<>();
public Map<ExportSettingsCategory, List<ExportDataObject>> getAdditionalData(boolean globalExport) {
Map<ExportSettingsCategory, List<ExportDataObject>> dataList = new LinkedHashMap<>();
List<ExportDataObject> settingsItems = getSettingsItems(globalExport);
List<ExportDataObject> myPlacesItems = getMyPlacesItems();
List<ExportDataObject> resourcesItems = getResourcesItems();
if (!settingsItems.isEmpty()) {
sortExportSettingsObjects(settingsItems);
dataList.put(ExportSettingsCategory.SETTINGS, settingsItems);
}
if (!myPlacesItems.isEmpty()) {
sortExportSettingsObjects(myPlacesItems);
dataList.put(ExportSettingsCategory.MY_PLACES, myPlacesItems);
}
if (!resourcesItems.isEmpty()) {
sortExportSettingsObjects(resourcesItems);
dataList.put(ExportSettingsCategory.RESOURCES, resourcesItems);
}
return dataList;
}
private List<ExportDataObject> getSettingsItems(boolean globalExport) {
List<ExportDataObject> settingsItems = new ArrayList<>();
if (globalExport) {
List<ApplicationModeBean> appModeBeans = new ArrayList<>();
for (ApplicationMode mode : ApplicationMode.allPossibleValues()) {
appModeBeans.add(mode.toModeBean());
}
settingsItems.add(new ExportDataObject(ExportSettingsType.PROFILE, appModeBeans));
}
settingsItems.add(new ExportDataObject(ExportSettingsType.GLOBAL, Collections.singletonList(new GlobalSettingsItem(app.getSettings()))));
QuickActionRegistry registry = app.getQuickActionRegistry();
List<QuickAction> actionsList = registry.getQuickActions();
if (!actionsList.isEmpty()) {
dataList.put(ExportSettingsType.QUICK_ACTIONS, actionsList);
settingsItems.add(new ExportDataObject(ExportSettingsType.QUICK_ACTIONS, actionsList));
}
List<PoiUIFilter> poiList = app.getPoiFilters().getUserDefinedPoiFilters(false);
if (!poiList.isEmpty()) {
dataList.put(ExportSettingsType.POI_TYPES, poiList);
settingsItems.add(new ExportDataObject(ExportSettingsType.POI_TYPES, poiList));
}
List<HistoryEntry> historyEntries = SearchHistoryHelper.getInstance(app).getHistoryEntries(false);
if (!historyEntries.isEmpty()) {
settingsItems.add(new ExportDataObject(ExportSettingsType.SEARCH_HISTORY, historyEntries));
}
Map<LatLon, AvoidRoadInfo> impassableRoads = app.getAvoidSpecificRoads().getImpassableRoads();
if (!impassableRoads.isEmpty()) {
settingsItems.add(new ExportDataObject(ExportSettingsType.AVOID_ROADS, new ArrayList<>(impassableRoads.values())));
}
return settingsItems;
}
private List<ExportDataObject> getMyPlacesItems() {
List<ExportDataObject> myPlacesItems = new ArrayList<>();
List<FavoriteGroup> favoriteGroups = app.getFavorites().getFavoriteGroups();
if (!favoriteGroups.isEmpty()) {
myPlacesItems.add(new ExportDataObject(ExportSettingsType.FAVORITES, favoriteGroups));
}
File gpxDir = app.getAppPath(IndexConstants.GPX_INDEX_DIR);
List<GPXInfo> gpxInfoList = GpxUiHelper.getSortedGPXFilesInfo(gpxDir, null, true);
if (!gpxInfoList.isEmpty()) {
List<File> files = new ArrayList<>();
for (GPXInfo gpxInfo : gpxInfoList) {
File file = new File(gpxInfo.getFileName());
if (file.exists()) {
files.add(file);
}
}
if (!files.isEmpty()) {
myPlacesItems.add(new ExportDataObject(ExportSettingsType.TRACKS, files));
}
}
OsmEditingPlugin osmEditingPlugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class);
if (osmEditingPlugin != null) {
List<OsmNotesPoint> notesPointList = osmEditingPlugin.getDBBug().getOsmbugsPoints();
if (!notesPointList.isEmpty()) {
myPlacesItems.add(new ExportDataObject(ExportSettingsType.OSM_NOTES, notesPointList));
}
List<OpenstreetmapPoint> editsPointList = osmEditingPlugin.getDBPOI().getOpenstreetmapPoints();
if (!editsPointList.isEmpty()) {
myPlacesItems.add(new ExportDataObject(ExportSettingsType.OSM_EDITS, editsPointList));
}
}
AudioVideoNotesPlugin plugin = OsmandPlugin.getPlugin(AudioVideoNotesPlugin.class);
if (plugin != null) {
List<File> files = new ArrayList<>();
for (Recording rec : plugin.getAllRecordings()) {
File file = rec.getFile();
if (file != null && file.exists()) {
files.add(file);
}
}
if (!files.isEmpty()) {
myPlacesItems.add(new ExportDataObject(ExportSettingsType.MULTIMEDIA_NOTES, files));
}
}
List<MapMarker> mapMarkers = app.getMapMarkersHelper().getMapMarkersFromDefaultGroups(false);
if (!mapMarkers.isEmpty()) {
String name = app.getString(R.string.map_markers);
String groupId = ExportSettingsType.ACTIVE_MARKERS.name();
MapMarkersGroup markersGroup = new MapMarkersGroup(groupId, name, MapMarkersGroup.ANY_TYPE);
markersGroup.setMarkers(mapMarkers);
myPlacesItems.add(new ExportDataObject(ExportSettingsType.ACTIVE_MARKERS, Collections.singletonList(markersGroup)));
}
List<MapMarker> markersHistory = app.getMapMarkersHelper().getMapMarkersFromDefaultGroups(true);
if (!markersHistory.isEmpty()) {
String name = app.getString(R.string.shared_string_history);
String groupId = ExportSettingsType.HISTORY_MARKERS.name();
MapMarkersGroup markersGroup = new MapMarkersGroup(groupId, name, MapMarkersGroup.ANY_TYPE);
markersGroup.setMarkers(markersHistory);
myPlacesItems.add(new ExportDataObject(ExportSettingsType.HISTORY_MARKERS, Collections.singletonList(markersGroup)));
}
return myPlacesItems;
}
private List<ExportDataObject> getResourcesItems() {
List<ExportDataObject> resourcesItems = new ArrayList<>();
Map<String, File> externalRenderers = app.getRendererRegistry().getExternalRenderers();
if (!externalRenderers.isEmpty()) {
resourcesItems.add(new ExportDataObject(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) {
resourcesItems.add(new ExportDataObject(ExportSettingsType.CUSTOM_ROUTING, Arrays.asList(fl)));
}
}
List<ITileSource> iTileSources = new ArrayList<>();
Set<String> tileSourceNames = app.getSettings().getTileSourceEntries(true).keySet();
for (String name : tileSourceNames) {
@ -515,111 +648,25 @@ public class SettingsHelper {
}
}
if (!iTileSources.isEmpty()) {
dataList.put(ExportSettingsType.MAP_SOURCES, iTileSources);
}
Map<String, File> 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<LatLon, AvoidRoadInfo> 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<File> 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<GPXInfo> gpxInfoList = GpxUiHelper.getSortedGPXFilesInfo(gpxDir, null, true);
if (!gpxInfoList.isEmpty()) {
List<File> 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);
}
}
if (globalExport) {
List<ApplicationModeBean> appModeBeans = new ArrayList<>();
for (ApplicationMode mode : ApplicationMode.allPossibleValues()) {
appModeBeans.add(mode.toModeBean());
}
dataList.put(ExportSettingsType.PROFILE, appModeBeans);
}
OsmEditingPlugin osmEditingPlugin = OsmandPlugin.getPlugin(OsmEditingPlugin.class);
if (osmEditingPlugin != null) {
List<OsmNotesPoint> notesPointList = osmEditingPlugin.getDBBug().getOsmbugsPoints();
if (!notesPointList.isEmpty()) {
dataList.put(ExportSettingsType.OSM_NOTES, notesPointList);
}
List<OpenstreetmapPoint> editsPointList = osmEditingPlugin.getDBPOI().getOpenstreetmapPoints();
if (!editsPointList.isEmpty()) {
dataList.put(ExportSettingsType.OSM_EDITS, editsPointList);
}
}
List<FavoriteGroup> favoriteGroups = app.getFavorites().getFavoriteGroups();
if (!favoriteGroups.isEmpty()) {
dataList.put(ExportSettingsType.FAVORITES, favoriteGroups);
resourcesItems.add(new ExportDataObject(ExportSettingsType.MAP_SOURCES, iTileSources));
}
List<LocalIndexInfo> localIndexInfoList = getLocalIndexData();
List<File> files = getFilesByType(localIndexInfoList, LocalIndexType.MAP_DATA, LocalIndexType.TILES_DATA,
LocalIndexType.SRTM_DATA, LocalIndexType.WIKI_DATA);
if (!files.isEmpty()) {
sortLocalFiles(files);
dataList.put(ExportSettingsType.OFFLINE_MAPS, files);
resourcesItems.add(new ExportDataObject(ExportSettingsType.OFFLINE_MAPS, files));
}
files = getFilesByType(localIndexInfoList, LocalIndexType.TTS_VOICE_DATA);
if (!files.isEmpty()) {
dataList.put(ExportSettingsType.TTS_VOICE, files);
resourcesItems.add(new ExportDataObject(ExportSettingsType.TTS_VOICE, files));
}
files = getFilesByType(localIndexInfoList, LocalIndexType.VOICE_DATA);
if (!files.isEmpty()) {
dataList.put(ExportSettingsType.VOICE, files);
resourcesItems.add(new ExportDataObject(ExportSettingsType.VOICE, files));
}
List<MapMarker> mapMarkers = app.getMapMarkersHelper().getMapMarkersFromDefaultGroups(false);
if (!mapMarkers.isEmpty()) {
String name = app.getString(R.string.map_markers);
String groupId = ExportSettingsType.ACTIVE_MARKERS.name();
MapMarkersGroup markersGroup = new MapMarkersGroup(groupId, name, MapMarkersGroup.ANY_TYPE);
markersGroup.setMarkers(mapMarkers);
dataList.put(ExportSettingsType.ACTIVE_MARKERS, Collections.singletonList(markersGroup));
}
List<MapMarker> markersHistory = app.getMapMarkersHelper().getMapMarkersFromDefaultGroups(true);
if (!markersHistory.isEmpty()) {
String name = app.getString(R.string.shared_string_history);
String groupId = ExportSettingsType.HISTORY_MARKERS.name();
MapMarkersGroup markersGroup = new MapMarkersGroup(groupId, name, MapMarkersGroup.ANY_TYPE);
markersGroup.setMarkers(markersHistory);
dataList.put(ExportSettingsType.HISTORY_MARKERS, Collections.singletonList(markersGroup));
}
List<HistoryEntry> historyEntries = SearchHistoryHelper.getInstance(app).getHistoryEntries(false);
if (!historyEntries.isEmpty()) {
dataList.put(ExportSettingsType.SEARCH_HISTORY, historyEntries);
}
return dataList;
return resourcesItems;
}
private List<LocalIndexInfo> getLocalIndexData() {
@ -745,6 +792,40 @@ public class SettingsHelper {
return settingsItems;
}
public static Map<ExportSettingsCategory, List<ExportDataObject>> getSettingsToOperateByCategory(List<SettingsItem> items, boolean importComplete) {
Map<ExportSettingsCategory, List<ExportDataObject>> exportMap = new LinkedHashMap<>();
Map<ExportSettingsType, List<?>> settingsToOperate = getSettingsToOperate(items, importComplete);
List<ExportDataObject> settingsItems = new ArrayList<>();
List<ExportDataObject> myPlacesItems = new ArrayList<>();
List<ExportDataObject> resourcesItems = new ArrayList<>();
for (Map.Entry<ExportSettingsType, List<?>> entry : settingsToOperate.entrySet()) {
ExportSettingsType type = entry.getKey();
if (type.isSettingsCategory()) {
settingsItems.add(new ExportDataObject(type, entry.getValue()));
} else if (type.isMyPlacesCategory()) {
myPlacesItems.add(new ExportDataObject(type, entry.getValue()));
} else if (type.isResourcesCategory()) {
resourcesItems.add(new ExportDataObject(type, entry.getValue()));
}
}
if (!settingsItems.isEmpty()) {
sortExportSettingsObjects(settingsItems);
exportMap.put(ExportSettingsCategory.SETTINGS, settingsItems);
}
if (!myPlacesItems.isEmpty()) {
sortExportSettingsObjects(myPlacesItems);
exportMap.put(ExportSettingsCategory.MY_PLACES, myPlacesItems);
}
if (!resourcesItems.isEmpty()) {
sortExportSettingsObjects(resourcesItems);
exportMap.put(ExportSettingsCategory.RESOURCES, resourcesItems);
}
return exportMap;
}
public static Map<ExportSettingsType, List<?>> getSettingsToOperate(List<SettingsItem> settingsItems, boolean importComplete) {
Map<ExportSettingsType, List<?>> settingsToOperate = new HashMap<>();
List<ApplicationModeBean> profiles = new ArrayList<>();
@ -913,7 +994,7 @@ public class SettingsHelper {
if (!markersGroups.isEmpty()) {
settingsToOperate.put(ExportSettingsType.ACTIVE_MARKERS, markersGroups);
}
if (!markersGroups.isEmpty()) {
if (!markersHistoryGroups.isEmpty()) {
settingsToOperate.put(ExportSettingsType.HISTORY_MARKERS, markersHistoryGroups);
}
if (!historyEntries.isEmpty()) {
@ -935,4 +1016,15 @@ public class SettingsHelper {
}
});
}
private static void sortExportSettingsObjects(List<ExportDataObject> items) {
Collections.sort(items, new Comparator<ExportDataObject>() {
@Override
public int compare(ExportDataObject lhs, ExportDataObject rhs) {
int order1 = lhs.getType().ordinal();
int order2 = rhs.getType().ordinal();
return (order1 < order2) ? -1 : ((order1 == order2) ? 0 : 1);
}
});
}
}

View file

@ -31,12 +31,9 @@ import androidx.recyclerview.widget.RecyclerView;
import net.osmand.AndroidUtils;
import net.osmand.IndexConstants;
import net.osmand.PlatformUtil;
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.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 +41,9 @@ 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.ApplicationMode;
import net.osmand.plus.settings.backend.backup.SettingsHelper;
import net.osmand.plus.settings.backend.backup.SettingsHelper.SettingsCollectListener;
import net.osmand.plus.settings.backend.backup.SettingsItem;
import net.osmand.plus.settings.bottomsheets.ResetProfilePrefsBottomSheet;
import net.osmand.plus.settings.bottomsheets.ResetProfilePrefsBottomSheet.ResetAppModePrefsListener;
@ -433,10 +433,7 @@ public class ConfigureProfileFragment extends BaseSettingsFragment implements Co
} else if (EXPORT_PROFILE.equals(prefId)) {
FragmentManager fragmentManager = getFragmentManager();
if (fragmentManager != null) {
ExportProfileBottomSheet.showInstance(
fragmentManager,
this,
getSelectedAppMode(), false);
ExportSettingsFragment.showInstance(fragmentManager, getSelectedAppMode(), false);
}
} else if (DELETE_PROFILE.equals(prefId)) {
onDeleteProfileClick();

View file

@ -0,0 +1,26 @@
package net.osmand.plus.settings.fragments;
import androidx.annotation.NonNull;
import net.osmand.plus.settings.backend.ExportSettingsType;
import java.util.List;
public class ExportDataObject {
private ExportSettingsType type;
private List<?> items;
public ExportDataObject(@NonNull ExportSettingsType type, @NonNull List<?> items) {
this.type = type;
this.items = items;
}
public ExportSettingsType getType() {
return type;
}
public List<?> getItems() {
return items;
}
}

View file

@ -1,429 +0,0 @@
package net.osmand.plus.settings.fragments;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.SwitchCompat;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import net.osmand.AndroidUtils;
import net.osmand.FileUtils;
import net.osmand.IndexConstants;
import net.osmand.PlatformUtil;
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.BottomSheetItemWithCompoundButton;
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.backend.ExportSettingsType;
import net.osmand.plus.settings.backend.backup.FileSettingsItem;
import net.osmand.plus.settings.backend.backup.GlobalSettingsItem;
import net.osmand.plus.settings.backend.backup.ProfileSettingsItem;
import net.osmand.plus.settings.backend.backup.SettingsHelper.SettingsExportListener;
import net.osmand.plus.settings.backend.backup.SettingsItem;
import net.osmand.plus.settings.bottomsheets.BasePreferenceBottomSheet;
import org.apache.commons.logging.Log;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
public class ExportProfileBottomSheet extends BasePreferenceBottomSheet {
public static final String TAG = ExportProfileBottomSheet.class.getSimpleName();
private static final Log LOG = PlatformUtil.getLog(ExportProfileBottomSheet.class);
private static final String GLOBAL_EXPORT_KEY = "global_export_key";
private static final String EXPORT_START_TIME_KEY = "export_start_time_key";
private static final String EXPORTING_PROFILE_KEY = "exporting_profile_key";
private static final String INCLUDE_ADDITIONAL_DATA_KEY = "include_additional_data_key";
private static final String INCLUDE_GLOBAL_SETTINGS_KEY = "include_global_settings_key";
private static final String PROGRESS_MAX_KEY = "progress_max_key";
private static final String PROGRESS_VALUE_KEY = "progress_value_key";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yy", Locale.US);
private OsmandApplication app;
private Map<ExportSettingsType, List<?>> dataList = new HashMap<>();
private ExportImportSettingsAdapter adapter;
private SettingsExportListener exportListener;
private ProgressDialog progress;
private int progressMax;
private int progressValue;
private long exportStartTime;
private boolean globalExport = false;
private boolean exportingProfile = false;
private boolean includeAdditionalData = false;
private boolean includeGlobalSettings = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = requiredMyApplication();
if (savedInstanceState != null) {
globalExport = savedInstanceState.getBoolean(GLOBAL_EXPORT_KEY);
exportingProfile = savedInstanceState.getBoolean(EXPORTING_PROFILE_KEY);
includeAdditionalData = savedInstanceState.getBoolean(INCLUDE_ADDITIONAL_DATA_KEY);
includeGlobalSettings = savedInstanceState.getBoolean(INCLUDE_GLOBAL_SETTINGS_KEY);
exportStartTime = savedInstanceState.getLong(EXPORT_START_TIME_KEY);
progressMax = savedInstanceState.getInt(PROGRESS_MAX_KEY);
progressValue = savedInstanceState.getInt(PROGRESS_VALUE_KEY);
}
dataList = app.getSettingsHelper().getAdditionalData(globalExport);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(GLOBAL_EXPORT_KEY, globalExport);
outState.putBoolean(EXPORTING_PROFILE_KEY, exportingProfile);
outState.putBoolean(INCLUDE_ADDITIONAL_DATA_KEY, includeAdditionalData);
outState.putBoolean(INCLUDE_GLOBAL_SETTINGS_KEY, includeGlobalSettings);
outState.putLong(EXPORT_START_TIME_KEY, exportStartTime);
outState.putInt(PROGRESS_MAX_KEY, progress.getMax());
outState.putInt(PROGRESS_VALUE_KEY, progress.getProgress());
}
@Override
public void createMenuItems(Bundle savedInstanceState) {
final Context context = getContext();
if (context == null) {
return;
}
if (globalExport) {
items.add(new TitleItem(getString(R.string.shared_string_export)));
final BottomSheetItemWithCompoundButton[] globalSettingsItem = new BottomSheetItemWithCompoundButton[1];
globalSettingsItem[0] = (BottomSheetItemWithCompoundButton) new BottomSheetItemWithCompoundButton.Builder()
.setChecked(includeGlobalSettings)
.setTitle(getString(R.string.general_settings_2))
.setLayoutId(R.layout.bottom_sheet_item_with_switch_no_icon)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean checked = !globalSettingsItem[0].isChecked();
globalSettingsItem[0].setChecked(checked);
includeGlobalSettings = checked;
}
})
.create();
items.add(globalSettingsItem[0]);
} else {
items.add(new TitleItem(getString(R.string.export_profile)));
ApplicationMode profile = getAppMode();
int profileColor = profile.getIconColorInfo().getColor(nightMode);
int colorNoAlpha = ContextCompat.getColor(context, profileColor);
Drawable backgroundIcon = UiUtilities.getColoredSelectableDrawable(context, colorNoAlpha, 0.3f);
Drawable[] layers = {new ColorDrawable(UiUtilities.getColorWithAlpha(colorNoAlpha, 0.10f)), backgroundIcon};
BaseBottomSheetItem profileItem = new BottomSheetItemWithCompoundButton.Builder()
.setChecked(true)
.setCompoundButtonColorId(profileColor)
.setButtonTintList(ColorStateList.valueOf(getResolvedColor(profileColor)))
.setDescription(BaseSettingsFragment.getAppModeDescription(context, profile))
.setIcon(getIcon(profile.getIconRes(), profileColor))
.setTitle(profile.toHumanString())
.setBackground(new LayerDrawable(layers))
.setLayoutId(R.layout.preference_profile_item_with_radio_btn)
.create();
items.add(profileItem);
}
setupAdditionalItems();
}
private void setupAdditionalItems() {
if (!dataList.isEmpty()) {
LayoutInflater inflater = UiUtilities.getInflater(app, nightMode);
View additionalDataView = inflater.inflate(R.layout.bottom_sheet_item_additional_data, null);
ExpandableListView listView = additionalDataView.findViewById(R.id.list);
adapter = new ExportImportSettingsAdapter(app, nightMode, false);
View listHeader = inflater.inflate(R.layout.item_header_export_expand_list, null);
final View topSwitchDivider = listHeader.findViewById(R.id.topSwitchDivider);
final View bottomSwitchDivider = listHeader.findViewById(R.id.bottomSwitchDivider);
final SwitchCompat switchItem = listHeader.findViewById(R.id.switchItem);
switchItem.setTextColor(getResources().getColor(nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light));
switchItem.setChecked(includeAdditionalData);
switchItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
includeAdditionalData = !includeAdditionalData;
topSwitchDivider.setVisibility(includeAdditionalData ? View.VISIBLE : View.GONE);
bottomSwitchDivider.setVisibility(includeAdditionalData ? View.VISIBLE : View.GONE);
if (includeAdditionalData) {
adapter.updateSettingsList(app.getSettingsHelper().getAdditionalData(globalExport));
adapter.selectAll(true);
} else {
adapter.selectAll(false);
adapter.clearSettingsList();
}
updateSwitch(switchItem);
setupHeightAndBackground(getView());
}
});
listView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
@Override
public void onGroupExpand(int i) {
setupHeightAndBackground(getView());
}
});
updateSwitch(switchItem);
listView.addHeaderView(listHeader);
listView.setAdapter(adapter);
final SimpleBottomSheetItem titleItem = (SimpleBottomSheetItem) new SimpleBottomSheetItem.Builder()
.setCustomView(additionalDataView)
.create();
items.add(titleItem);
}
}
private void updateSwitch(View view) {
if (includeAdditionalData) {
UiUtilities.setMargins(view, 0, 0, 0, 0);
view.setPadding(AndroidUtils.dpToPx(app, 32), 0, AndroidUtils.dpToPx(app, 32), 0);
} else {
UiUtilities.setMargins(view, AndroidUtils.dpToPx(app, 16), 0, AndroidUtils.dpToPx(app, 16), 0);
view.setPadding(AndroidUtils.dpToPx(app, 16), 0, AndroidUtils.dpToPx(app, 16), 0);
}
}
@Override
protected int getRightBottomButtonTextId() {
return R.string.shared_string_export;
}
@Override
protected void onRightBottomButtonClick() {
prepareFile();
}
@Override
protected int getDismissButtonTextId() {
return R.string.shared_string_cancel;
}
@Override
protected boolean useScrollableItemsContainer() {
return false;
}
@Override
protected boolean useExpandableList() {
return true;
}
@Override
public void onResume() {
super.onResume();
checkExportingFile();
}
@Override
public void onPause() {
super.onPause();
if (exportingProfile) {
File file = getExportFile();
app.getSettingsHelper().updateExportListener(file, null);
}
}
private void setGlobalExport(boolean globalExport) {
this.globalExport = globalExport;
}
private List<SettingsItem> prepareSettingsItemsForExport() {
List<SettingsItem> settingsItems = new ArrayList<>();
if (!globalExport) {
settingsItems.add(new ProfileSettingsItem(app, getAppMode()));
}
if (includeGlobalSettings) {
settingsItems.add(new GlobalSettingsItem(app.getSettings()));
}
if (includeAdditionalData) {
settingsItems.addAll(app.getSettingsHelper().prepareAdditionalSettingsItems(adapter.getData()));
}
return settingsItems;
}
private void prepareFile() {
if (app != null) {
exportingProfile = true;
exportStartTime = System.currentTimeMillis();
showExportProgressDialog();
File tempDir = FileUtils.getTempDir(app);
String fileName = getFileName();
List<SettingsItem> items = prepareSettingsItemsForExport();
progress.setMax(getMaxProgress(items));
app.getSettingsHelper().exportSettings(tempDir, fileName, getSettingsExportListener(), items, true);
}
}
private int getMaxProgress(List<SettingsItem> items) {
long maxProgress = 0;
for (SettingsItem item : items) {
if (item instanceof FileSettingsItem) {
maxProgress += ((FileSettingsItem) item).getSize();
}
}
return (int) maxProgress / (1 << 20);
}
private String getFileName() {
if (globalExport) {
if (exportStartTime == 0) {
exportStartTime = System.currentTimeMillis();
}
return "Export_" + DATE_FORMAT.format(new Date(exportStartTime));
} else {
return getAppMode().toHumanString();
}
}
private void showExportProgressDialog() {
Context context = getContext();
if (context == null) {
return;
}
if (progress != null) {
progress.dismiss();
}
progress = new ProgressDialog(context);
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progress.setCancelable(true);
progress.setTitle(app.getString(R.string.shared_string_export));
progress.setMessage(app.getString(R.string.shared_string_preparing));
progress.setButton(DialogInterface.BUTTON_NEGATIVE, app.getString(R.string.shared_string_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
cancelExport();
}
});
progress.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancelExport();
}
});
progress.show();
}
private void cancelExport() {
app.getSettingsHelper().cancelExportForFile(getExportFile());
progress.dismiss();
dismiss();
}
private SettingsExportListener getSettingsExportListener() {
if (exportListener == null) {
exportListener = new SettingsExportListener() {
@Override
public void onSettingsExportFinished(@NonNull File file, boolean succeed) {
dismissExportProgressDialog();
exportingProfile = false;
if (succeed) {
shareProfile(file);
} else {
app.showToastMessage(R.string.export_profile_failed);
}
}
@Override
public void onSettingsExportProgressUpdate(int value) {
progress.setProgress(value);
}
};
}
return exportListener;
}
private void checkExportingFile() {
if (exportingProfile) {
File file = getExportFile();
boolean fileExporting = app.getSettingsHelper().isFileExporting(file);
if (fileExporting) {
showExportProgressDialog();
progress.setMax(progressMax);
progress.setProgress(progressValue);
app.getSettingsHelper().updateExportListener(file, getSettingsExportListener());
} else if (file.exists()) {
dismissExportProgressDialog();
shareProfile(file);
}
}
}
private void dismissExportProgressDialog() {
FragmentActivity activity = getActivity();
if (progress != null && activity != null && AndroidUtils.isActivityNotDestroyed(activity)) {
progress.dismiss();
}
}
private File getExportFile() {
File tempDir = FileUtils.getTempDir(app);
String fileName = getFileName();
return new File(tempDir, fileName + IndexConstants.OSMAND_SETTINGS_FILE_EXT);
}
private void shareProfile(@NonNull File file) {
try {
final Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, file.getName());
sendIntent.putExtra(Intent.EXTRA_STREAM, AndroidUtils.getUriForFile(getMyApplication(), file));
sendIntent.setType("*/*");
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(sendIntent);
dismiss();
} catch (Exception e) {
Toast.makeText(requireContext(), R.string.export_profile_failed, Toast.LENGTH_SHORT).show();
LOG.error("Share profile error", e);
}
}
public static boolean showInstance(@NonNull FragmentManager fragmentManager,
Fragment target, @NonNull ApplicationMode appMode,
boolean globalExport) {
try {
ExportProfileBottomSheet fragment = new ExportProfileBottomSheet();
fragment.setAppMode(appMode);
fragment.setGlobalExport(globalExport);
fragment.setTargetFragment(target, 0);
fragment.show(fragmentManager, TAG);
return true;
} catch (RuntimeException e) {
return false;
}
}
}

View file

@ -0,0 +1,341 @@
package net.osmand.plus.settings.fragments;
import android.content.res.ColorStateList;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.core.widget.CompoundButtonCompat;
import net.osmand.AndroidUtils;
import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.OsmandBaseExpandableListAdapter;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.helpers.FontCache;
import net.osmand.plus.settings.backend.ExportSettingsCategory;
import net.osmand.plus.settings.backend.ExportSettingsType;
import net.osmand.plus.settings.backend.backup.FileSettingsItem;
import net.osmand.view.ThreeStateCheckbox;
import org.apache.commons.logging.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static net.osmand.view.ThreeStateCheckbox.State.CHECKED;
import static net.osmand.view.ThreeStateCheckbox.State.MISC;
import static net.osmand.view.ThreeStateCheckbox.State.UNCHECKED;
public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter {
private static final Log LOG = PlatformUtil.getLog(ExportImportSettingsAdapter.class.getName());
private final OsmandApplication app;
private final UiUtilities uiUtilities;
private List<ExportSettingsCategory> itemsTypes = new ArrayList<>();
private Map<ExportSettingsType, List<?>> selectedItemsMap = new HashMap<>();
private Map<ExportSettingsCategory, List<ExportDataObject>> itemsMap = new LinkedHashMap<>();
private final OnItemSelectedListener listener;
private final LayoutInflater themedInflater;
private final boolean nightMode;
private final int activeColorRes;
private final int secondaryColorRes;
ExportSettingsAdapter(OsmandApplication app, OnItemSelectedListener listener, boolean nightMode) {
this.app = app;
this.listener = listener;
this.nightMode = nightMode;
uiUtilities = app.getUIUtilities();
themedInflater = UiUtilities.getInflater(app, nightMode);
activeColorRes = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light;
secondaryColorRes = nightMode ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
View group = convertView;
if (group == null) {
group = themedInflater.inflate(R.layout.profile_data_list_item_group, parent, false);
}
final ExportSettingsCategory category = itemsTypes.get(groupPosition);
final List<ExportDataObject> items = itemsMap.get(category);
String title = app.getString(category.getTitleId());
TextView titleTv = group.findViewById(R.id.title_tv);
titleTv.setText(UiUtilities.createCustomFontSpannable(FontCache.getRobotoMedium(app), title, title));
TextView subTextTv = group.findViewById(R.id.sub_text_tv);
subTextTv.setText(getCategoryDescr(category));
int selectedTypes = 0;
for (int i = 0; i < items.size(); i++) {
ExportDataObject object = items.get(i);
if (selectedItemsMap.containsKey(object.getType())) {
selectedTypes++;
}
}
final ThreeStateCheckbox checkBox = group.findViewById(R.id.check_box);
if (selectedTypes == 0) {
checkBox.setState(UNCHECKED);
} else {
checkBox.setState(selectedTypes == items.size() ? CHECKED : MISC);
}
int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes;
CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor)));
group.findViewById(R.id.check_box_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkBox.performClick();
boolean selected = checkBox.getState() == CHECKED;
if (selected) {
for (ExportDataObject object : items) {
if (!selectedItemsMap.containsKey(object.getType())) {
selectedItemsMap.put(object.getType(), object.getItems());
}
}
} else {
for (ExportDataObject object : items) {
selectedItemsMap.remove(object.getType());
}
}
if (listener != null) {
listener.onCategorySelected(category, selected);
}
notifyDataSetChanged();
}
});
adjustIndicator(app, groupPosition, isExpanded, group, nightMode);
AndroidUiHelper.updateVisibility(group.findViewById(R.id.divider), isExpanded);
AndroidUiHelper.updateVisibility(group.findViewById(R.id.card_top_divider), true);
AndroidUiHelper.updateVisibility(group.findViewById(R.id.vertical_divider), false);
AndroidUiHelper.updateVisibility(group.findViewById(R.id.card_bottom_divider), !isExpanded);
return group;
}
@Override
public View getChildView(int groupPosition, final int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
View child = convertView;
if (child == null) {
child = themedInflater.inflate(R.layout.profile_data_list_item_group, parent, false);
}
final ExportDataObject currentItem = itemsMap.get(itemsTypes.get(groupPosition)).get(childPosition);
List<?> selectedItems = selectedItemsMap.get(currentItem.getType());
TextView titleTv = child.findViewById(R.id.title_tv);
titleTv.setText(currentItem.getType().getTitleId());
TextView subTextTv = child.findViewById(R.id.sub_text_tv);
subTextTv.setText(getSelectedTypeDescr(currentItem));
ImageView icon = child.findViewById(R.id.explist_indicator);
setupIcon(icon, currentItem.getType().getIconRes(), selectedItems != null);
final ThreeStateCheckbox checkBox = child.findViewById(R.id.check_box);
if (selectedItems == null) {
checkBox.setState(UNCHECKED);
} else if (selectedItems.containsAll(currentItem.getItems())) {
checkBox.setState(CHECKED);
} else {
boolean contains = false;
for (Object object : currentItem.getItems()) {
if (selectedItems.contains(object)) {
contains = true;
break;
}
}
checkBox.setState(contains ? MISC : UNCHECKED);
}
int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes;
CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor)));
child.findViewById(R.id.check_box_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
checkBox.performClick();
boolean selected = checkBox.getState() == CHECKED;
if (selected) {
selectedItemsMap.put(currentItem.getType(), currentItem.getItems());
} else {
selectedItemsMap.remove(currentItem.getType());
}
if (listener != null) {
listener.onTypeSelected(currentItem.getType(), selected);
}
notifyDataSetChanged();
}
});
AndroidUiHelper.updateVisibility(child.findViewById(R.id.card_bottom_divider), isLastChild);
return child;
}
@Override
public int getGroupCount() {
return itemsTypes.size();
}
@Override
public int getChildrenCount(int i) {
return itemsMap.get(itemsTypes.get(i)).size();
}
@Override
public Object getGroup(int i) {
return itemsMap.get(itemsTypes.get(i));
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return itemsMap.get(itemsTypes.get(groupPosition)).get(childPosition);
}
@Override
public long getGroupId(int i) {
return i;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return groupPosition * 10000 + childPosition;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public boolean isChildSelectable(int i, int i1) {
return true;
}
private void setupIcon(ImageView icon, int iconRes, boolean itemSelected) {
if (itemSelected) {
int colorRes = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_osmand_light;
icon.setImageDrawable(uiUtilities.getIcon(iconRes, colorRes));
} else {
icon.setImageDrawable(uiUtilities.getIcon(iconRes, secondaryColorRes));
}
}
public void updateSettingsList(Map<ExportSettingsCategory, List<ExportDataObject>> itemsMap) {
this.itemsMap = itemsMap;
this.itemsTypes = new ArrayList<>(itemsMap.keySet());
Collections.sort(itemsTypes);
notifyDataSetChanged();
}
public void clearSettingsList() {
this.itemsMap.clear();
this.itemsTypes.clear();
notifyDataSetChanged();
}
public boolean hasSelectedData() {
return !selectedItemsMap.isEmpty();
}
public List<? super Object> getData() {
List<Object> selectedItems = new ArrayList<>();
for (List<?> items : selectedItemsMap.values()) {
selectedItems.addAll(items);
}
return selectedItems;
}
private String getCategoryDescr(ExportSettingsCategory category) {
long itemsSize = 0;
int selectedTypes = 0;
List<ExportDataObject> items = itemsMap.get(category);
for (int i = 0; i < items.size(); i++) {
ExportDataObject object = items.get(i);
if (selectedItemsMap.containsKey(object.getType())) {
selectedTypes++;
itemsSize += calculateItemsSize(object.getItems());
}
}
String description;
if (selectedTypes == 0) {
description = app.getString(R.string.shared_string_none);
} else if (selectedTypes == items.size()) {
description = app.getString(R.string.shared_string_all);
} else {
description = app.getString(R.string.ltr_or_rtl_combine_via_slash, String.valueOf(selectedTypes), String.valueOf(items.size()));
}
String formattedSize = AndroidUtils.formatSize(app, itemsSize);
return itemsSize == 0 ? description : app.getString(R.string.ltr_or_rtl_combine_via_comma, description, formattedSize);
}
public static long calculateItemsSize(List<?> items) {
long itemsSize = 0;
for (Object item : items) {
if (item instanceof FileSettingsItem) {
itemsSize += ((FileSettingsItem) item).getSize();
} else if (item instanceof File) {
itemsSize += ((File) item).length();
}
}
return itemsSize;
}
private String getSelectedTypeDescr(ExportDataObject dataObject) {
long itemsSize = 0;
int selectedTypes = 0;
List<?> items = dataObject.getItems();
List<?> selectedItems = selectedItemsMap.get(dataObject.getType());
if (selectedItems != null) {
for (int i = 0; i < items.size(); i++) {
Object object = items.get(i);
if (selectedItems.contains(object)) {
selectedTypes++;
if (object instanceof FileSettingsItem) {
itemsSize += ((FileSettingsItem) object).getSize();
} else if (object instanceof File) {
itemsSize += ((File) object).length();
}
}
}
}
String description;
if (selectedTypes == 0) {
description = app.getString(R.string.shared_string_none);
} else if (selectedTypes == items.size()) {
description = app.getString(R.string.shared_string_all);
if (itemsSize == 0) {
description = app.getString(R.string.ltr_or_rtl_combine_via_comma, description, String.valueOf(items.size()));
}
} else {
description = app.getString(R.string.ltr_or_rtl_combine_via_slash, String.valueOf(selectedTypes), String.valueOf(items.size()));
}
String formattedSize = AndroidUtils.formatSize(app, itemsSize);
return itemsSize == 0 ? description : app.getString(R.string.ltr_or_rtl_combine_via_comma, description, formattedSize);
}
interface OnItemSelectedListener {
void onCategorySelected(ExportSettingsCategory type, boolean selected);
void onTypeSelected(ExportSettingsType type, boolean selected);
}
}

View file

@ -0,0 +1,455 @@
package net.osmand.plus.settings.fragments;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ExpandableListView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.google.android.material.appbar.CollapsingToolbarLayout;
import net.osmand.AndroidUtils;
import net.osmand.FileUtils;
import net.osmand.IndexConstants;
import net.osmand.PlatformUtil;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.UiUtilities.DialogButtonType;
import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.settings.backend.ApplicationMode;
import net.osmand.plus.settings.backend.ExportSettingsCategory;
import net.osmand.plus.settings.backend.ExportSettingsType;
import net.osmand.plus.settings.backend.backup.FileSettingsItem;
import net.osmand.plus.settings.backend.backup.SettingsHelper.SettingsExportListener;
import net.osmand.plus.settings.backend.backup.SettingsItem;
import net.osmand.plus.settings.fragments.ExportSettingsAdapter.OnItemSelectedListener;
import net.osmand.plus.widgets.TextViewEx;
import org.apache.commons.logging.Log;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static net.osmand.plus.settings.fragments.BaseSettingsFragment.APP_MODE_KEY;
public class ExportSettingsFragment extends BaseOsmAndFragment implements OnItemSelectedListener {
public static final String TAG = ImportSettingsFragment.class.getSimpleName();
public static final Log LOG = PlatformUtil.getLog(ImportSettingsFragment.class.getSimpleName());
private static final String EXPORT_SETTINGS_TAG = "import_settings_tag";
private static final String GLOBAL_EXPORT_KEY = "global_export_key";
private static final String EXPORT_START_TIME_KEY = "export_start_time_key";
private static final String EXPORTING_STARTED_KEY = "exporting_started_key";
private static final String INCLUDE_ADDITIONAL_DATA_KEY = "include_additional_data_key";
private static final String INCLUDE_GLOBAL_SETTINGS_KEY = "include_global_settings_key";
private static final String PROGRESS_MAX_KEY = "progress_max_key";
private static final String PROGRESS_VALUE_KEY = "progress_value_key";
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd-MM-yy", Locale.US);
private OsmandApplication app;
private Map<ExportSettingsCategory, List<ExportDataObject>> dataList;
private ProgressDialog progress;
private ApplicationMode appMode;
private SettingsExportListener exportListener;
private View continueBtn;
private View headerShadow;
private View headerDivider;
private View itemsSizeContainer;
private View availableSpaceContainer;
private TextViewEx selectedItemsSize;
private TextViewEx availableSpaceDescr;
private LinearLayout buttonsContainer;
private ExpandableListView expandableList;
private ExportSettingsAdapter adapter;
private int progressMax;
private int progressValue;
private long exportStartTime;
private boolean nightMode;
private boolean globalExport;
private boolean exportingStarted;
@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);
app = requireMyApplication();
nightMode = !app.getSettings().isLightContent();
if (savedInstanceState != null) {
appMode = ApplicationMode.valueOfStringKey(savedInstanceState.getString(APP_MODE_KEY), null);
globalExport = savedInstanceState.getBoolean(GLOBAL_EXPORT_KEY);
exportingStarted = savedInstanceState.getBoolean(EXPORTING_STARTED_KEY);
exportStartTime = savedInstanceState.getLong(EXPORT_START_TIME_KEY);
progressMax = savedInstanceState.getInt(PROGRESS_MAX_KEY);
progressValue = savedInstanceState.getInt(PROGRESS_VALUE_KEY);
}
dataList = app.getSettingsHelper().getAdditionalData(globalExport);
requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
showExitDialog();
}
});
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
LayoutInflater themedInflater = UiUtilities.getInflater(app, nightMode);
View root = themedInflater.inflate(R.layout.fragment_import, container, false);
AndroidUtils.addStatusBarPadding21v(app, root);
selectedItemsSize = root.findViewById(R.id.file_size);
itemsSizeContainer = root.findViewById(R.id.file_size_container);
expandableList = root.findViewById(R.id.list);
buttonsContainer = root.findViewById(R.id.buttons_container);
Toolbar toolbar = root.findViewById(R.id.toolbar);
setupToolbar(toolbar);
ViewCompat.setNestedScrollingEnabled(expandableList, true);
View header = themedInflater.inflate(R.layout.list_item_description_header, null);
headerDivider = header.findViewById(R.id.divider);
headerShadow = header.findViewById(R.id.card_bottom_divider);
expandableList.addHeaderView(header);
availableSpaceContainer = inflater.inflate(R.layout.enough_space_warning_card, null);
availableSpaceDescr = availableSpaceContainer.findViewById(R.id.warning_descr);
continueBtn = root.findViewById(R.id.continue_button);
UiUtilities.setupDialogButton(nightMode, continueBtn, DialogButtonType.PRIMARY, getString(R.string.shared_string_continue));
continueBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
prepareFile();
}
});
ViewTreeObserver treeObserver = buttonsContainer.getViewTreeObserver();
treeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (buttonsContainer != null) {
ViewTreeObserver vts = buttonsContainer.getViewTreeObserver();
int height = buttonsContainer.getMeasuredHeight();
expandableList.setPadding(0, 0, 0, height);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
vts.removeOnGlobalLayoutListener(this);
} else {
vts.removeGlobalOnLayoutListener(this);
}
}
}
});
adapter = new ExportSettingsAdapter(app, this, nightMode);
adapter.updateSettingsList(dataList);
expandableList.setAdapter(adapter);
CollapsingToolbarLayout toolbarLayout = root.findViewById(R.id.toolbar_layout);
toolbarLayout.setTitle(getString(R.string.shared_string_export));
TextView description = header.findViewById(R.id.description);
description.setText(R.string.select_data_to_export);
updateAvailableSpace();
return root;
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(GLOBAL_EXPORT_KEY, globalExport);
outState.putBoolean(EXPORTING_STARTED_KEY, exportingStarted);
outState.putLong(EXPORT_START_TIME_KEY, exportStartTime);
outState.putString(APP_MODE_KEY, appMode.getStringKey());
if (progress != null) {
outState.putInt(PROGRESS_MAX_KEY, progress.getMax());
outState.putInt(PROGRESS_VALUE_KEY, progress.getProgress());
}
}
@Override
public void onResume() {
super.onResume();
checkExportingFile();
}
@Override
public void onPause() {
super.onPause();
if (exportingStarted) {
File file = getExportFile();
app.getSettingsHelper().updateExportListener(file, null);
}
}
private void dismissFragment() {
FragmentManager fm = getFragmentManager();
if (fm != null && !fm.isStateSaved()) {
getFragmentManager().popBackStack(EXPORT_SETTINGS_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
public void showExitDialog() {
Context themedContext = UiUtilities.getThemedContext(getActivity(), nightMode);
AlertDialog.Builder dismissDialog = new AlertDialog.Builder(themedContext);
dismissDialog.setTitle(getString(R.string.shared_string_dismiss));
dismissDialog.setMessage(getString(R.string.exit_without_saving));
dismissDialog.setNegativeButton(R.string.shared_string_cancel, null);
dismissDialog.setPositiveButton(R.string.shared_string_exit, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismissFragment();
}
});
dismissDialog.show();
}
private void setupToolbar(Toolbar toolbar) {
int color = ContextCompat.getColor(app, nightMode ? R.color.active_buttons_and_links_text_dark : R.color.active_buttons_and_links_text_light);
toolbar.setNavigationIcon(getPaintedContentIcon(R.drawable.ic_action_close, color));
toolbar.setNavigationContentDescription(R.string.shared_string_close);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showExitDialog();
}
});
}
private void updateAvailableSpace() {
long calculatedSize = ExportSettingsAdapter.calculateItemsSize(adapter.getData());
if (calculatedSize != 0) {
selectedItemsSize.setText(AndroidUtils.formatSize(app, calculatedSize));
File dir = app.getAppPath("").getParentFile();
long availableSizeBytes = AndroidUtils.getAvailableSpace(dir);
if (calculatedSize > availableSizeBytes) {
String availableSize = AndroidUtils.formatSize(app, availableSizeBytes);
availableSpaceDescr.setText(getString(R.string.export_not_enough_space_descr, availableSize));
updateWarningHeaderVisibility(true);
continueBtn.setEnabled(false);
} else {
updateWarningHeaderVisibility(false);
continueBtn.setEnabled(adapter.hasSelectedData());
}
itemsSizeContainer.setVisibility(View.VISIBLE);
} else {
updateWarningHeaderVisibility(false);
itemsSizeContainer.setVisibility(View.INVISIBLE);
continueBtn.setEnabled(adapter.hasSelectedData());
}
}
private void updateWarningHeaderVisibility(boolean visible) {
if (visible) {
if (expandableList.getHeaderViewsCount() < 2) {
expandableList.addHeaderView(availableSpaceContainer);
}
AndroidUiHelper.updateVisibility(headerShadow, false);
AndroidUiHelper.updateVisibility(headerDivider, true);
} else {
expandableList.removeHeaderView(availableSpaceContainer);
AndroidUiHelper.updateVisibility(headerShadow, true);
AndroidUiHelper.updateVisibility(headerDivider, false);
}
}
@Override
public void onCategorySelected(ExportSettingsCategory type, boolean selected) {
updateAvailableSpace();
}
@Override
public void onTypeSelected(ExportSettingsType type, boolean selected) {
updateAvailableSpace();
}
private void prepareFile() {
if (app != null) {
exportingStarted = true;
exportStartTime = System.currentTimeMillis();
showExportProgressDialog();
File tempDir = FileUtils.getTempDir(app);
String fileName = getFileName();
List<SettingsItem> items = app.getSettingsHelper().prepareAdditionalSettingsItems(adapter.getData());
progress.setMax(getMaxProgress(items));
app.getSettingsHelper().exportSettings(tempDir, fileName, getSettingsExportListener(), items, true);
}
}
private int getMaxProgress(List<SettingsItem> items) {
long maxProgress = 0;
for (SettingsItem item : items) {
if (item instanceof FileSettingsItem) {
maxProgress += ((FileSettingsItem) item).getSize();
}
}
return (int) maxProgress / (1 << 20);
}
private String getFileName() {
if (globalExport) {
if (exportStartTime == 0) {
exportStartTime = System.currentTimeMillis();
}
return "Export_" + DATE_FORMAT.format(new Date(exportStartTime));
} else {
return appMode.toHumanString();
}
}
private void showExportProgressDialog() {
Context context = getContext();
if (context == null) {
return;
}
if (progress != null) {
progress.dismiss();
}
progress = new ProgressDialog(context);
progress.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progress.setCancelable(true);
progress.setTitle(app.getString(R.string.shared_string_export));
progress.setMessage(app.getString(R.string.shared_string_preparing));
progress.setButton(DialogInterface.BUTTON_NEGATIVE, app.getString(R.string.shared_string_cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
cancelExport();
}
});
progress.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
cancelExport();
}
});
progress.show();
}
private void cancelExport() {
app.getSettingsHelper().cancelExportForFile(getExportFile());
progress.dismiss();
dismissFragment();
}
private SettingsExportListener getSettingsExportListener() {
if (exportListener == null) {
exportListener = new SettingsExportListener() {
@Override
public void onSettingsExportFinished(@NonNull File file, boolean succeed) {
dismissExportProgressDialog();
exportingStarted = false;
if (succeed) {
shareProfile(file);
} else {
app.showToastMessage(R.string.export_profile_failed);
}
}
@Override
public void onSettingsExportProgressUpdate(int value) {
progress.setProgress(value);
}
};
}
return exportListener;
}
private void checkExportingFile() {
if (exportingStarted) {
File file = getExportFile();
boolean fileExporting = app.getSettingsHelper().isFileExporting(file);
if (fileExporting) {
showExportProgressDialog();
progress.setMax(progressMax);
progress.setProgress(progressValue);
app.getSettingsHelper().updateExportListener(file, getSettingsExportListener());
} else if (file.exists()) {
dismissExportProgressDialog();
shareProfile(file);
}
}
}
private void dismissExportProgressDialog() {
FragmentActivity activity = getActivity();
if (progress != null && activity != null && AndroidUtils.isActivityNotDestroyed(activity)) {
progress.dismiss();
}
}
private File getExportFile() {
File tempDir = FileUtils.getTempDir(app);
String fileName = getFileName();
return new File(tempDir, fileName + IndexConstants.OSMAND_SETTINGS_FILE_EXT);
}
private void shareProfile(@NonNull File file) {
try {
final Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, file.getName());
sendIntent.putExtra(Intent.EXTRA_STREAM, AndroidUtils.getUriForFile(getMyApplication(), file));
sendIntent.setType("*/*");
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(sendIntent);
dismissFragment();
} catch (Exception e) {
Toast.makeText(requireContext(), R.string.export_profile_failed, Toast.LENGTH_SHORT).show();
LOG.error("Share profile error", e);
}
}
public static boolean showInstance(@NonNull FragmentManager fragmentManager, @NonNull ApplicationMode appMode, boolean globalExport) {
try {
ExportSettingsFragment fragment = new ExportSettingsFragment();
fragment.appMode = appMode;
fragment.globalExport = globalExport;
fragmentManager.beginTransaction().
replace(R.id.fragmentContainer, fragment, TAG)
.addToBackStack(EXPORT_SETTINGS_TAG)
.commitAllowingStateLoss();
return true;
} catch (RuntimeException e) {
return false;
}
}
}

View file

@ -90,7 +90,6 @@ public class ImportSettingsFragment extends BaseOsmAndFragment {
private OsmandApplication app;
private ExportImportSettingsAdapter adapter;
private ExpandableListView expandableList;
private TextViewEx selectBtn;
private TextView description;
private List<SettingsItem> settingsItems;
private File file;
@ -137,7 +136,6 @@ public class ImportSettingsFragment extends BaseOsmAndFragment {
Toolbar toolbar = root.findViewById(R.id.toolbar);
TextViewEx continueBtn = root.findViewById(R.id.continue_button);
toolbarLayout = root.findViewById(R.id.toolbar_layout);
selectBtn = root.findViewById(R.id.select_button);
expandableList = root.findViewById(R.id.list);
buttonsContainer = root.findViewById(R.id.buttons_container);
progressBar = root.findViewById(R.id.progress_bar);
@ -157,14 +155,6 @@ public class ImportSettingsFragment extends BaseOsmAndFragment {
}
}
});
selectBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
allSelected = !allSelected;
selectBtn.setText(allSelected ? R.string.shared_string_deselect_all : R.string.shared_string_select_all);
adapter.selectAll(allSelected);
}
});
if (Build.VERSION.SDK_INT >= 21) {
AndroidUtils.addStatusBarPadding21v(app, root);
}

View file

@ -153,7 +153,7 @@ public class MainSettingsFragment extends BaseSettingsFragment {
if (mapActivity != null) {
ApplicationMode mode = getSelectedAppMode();
FragmentManager fragmentManager = mapActivity.getSupportFragmentManager();
ExportProfileBottomSheet.showInstance(fragmentManager, this, mode, true);
ExportSettingsFragment.showInstance(fragmentManager, mode, true);
}
}
return super.onPreferenceClick(preference);