diff --git a/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java b/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java index af48552bc4..53e838a9a7 100644 --- a/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java +++ b/OsmAnd-api/src/net/osmand/aidlapi/OsmAndCustomizationConstants.java @@ -11,6 +11,7 @@ public interface OsmAndCustomizationConstants { String DRAWER_MY_PLACES_ID = DRAWER_ITEM_ID_SCHEME + "my_places"; String DRAWER_SEARCH_ID = DRAWER_ITEM_ID_SCHEME + "search"; String DRAWER_DIRECTIONS_ID = DRAWER_ITEM_ID_SCHEME + "directions"; + String DRAWER_TRIP_RECORDING_ID = DRAWER_ITEM_ID_SCHEME + "trip_recording"; String DRAWER_CONFIGURE_MAP_ID = DRAWER_ITEM_ID_SCHEME + "configure_map"; String DRAWER_DOWNLOAD_MAPS_ID = DRAWER_ITEM_ID_SCHEME + "download_maps"; String DRAWER_OSMAND_LIVE_ID = DRAWER_ITEM_ID_SCHEME + "osmand_live"; diff --git a/OsmAnd/res/layout/bottom_sheet_item_button_with_icon.xml b/OsmAnd/res/layout/bottom_sheet_item_button_with_icon.xml new file mode 100644 index 0000000000..3d2d1bbab1 --- /dev/null +++ b/OsmAnd/res/layout/bottom_sheet_item_button_with_icon.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/values/sizes.xml b/OsmAnd/res/values/sizes.xml index a086849297..afdc8438af 100644 --- a/OsmAnd/res/values/sizes.xml +++ b/OsmAnd/res/values/sizes.xml @@ -413,4 +413,6 @@ 32dp 24dp + + 0dp \ No newline at end of file diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 8d34bfdc28..6c7b5333dc 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -13,6 +13,10 @@ --> On pause + Are you sure you want to stop recording?\nAll unsaved data will be lost. + Track recording stopped + Save and stop recording + Stop without saving Login to OpenPlaceReviews Use test.openplacereviews.org OpenPlaceReviews diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java index 258ed7be38..625396e5e3 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityActions.java @@ -22,6 +22,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentActivity; import net.osmand.AndroidUtils; import net.osmand.GPXUtilities; @@ -62,6 +63,8 @@ import net.osmand.plus.mapmarkers.MarkersPlanRouteContext; import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.measurementtool.StartPlanRouteBottomSheet; import net.osmand.plus.monitoring.OsmandMonitoringPlugin; +import net.osmand.plus.monitoring.TripRecordingActiveBottomSheet; +import net.osmand.plus.monitoring.TripRecordingBottomSheet; import net.osmand.plus.osmedit.dialogs.DismissRouteBottomSheetFragment; import net.osmand.plus.profiles.ProfileDataObject; import net.osmand.plus.profiles.ProfileDataUtils; @@ -98,6 +101,7 @@ import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_CONFIGURE_P import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_CONFIGURE_SCREEN_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_DASHBOARD_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_DIRECTIONS_ID; +import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_TRIP_RECORDING_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_DIVIDER_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_DOWNLOAD_MAPS_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_HELP_ID; @@ -839,6 +843,22 @@ public class MapActivityActions implements DialogProvider { } }).createItem()); + boolean isTripRecordingPluginOn = OsmandPlugin.getEnabledPlugin(OsmandMonitoringPlugin.class) != null; + if (isTripRecordingPluginOn) { + optionsMenuHelper.addItem(new ItemBuilder().setTitleId(R.string.map_widget_monitoring, mapActivity) + .setId(DRAWER_TRIP_RECORDING_ID) + .setIcon(R.drawable.ic_action_track_recordable) + .setListener(new ItemClickListener() { + @Override + public boolean onContextMenuClick(ArrayAdapter adapter, int itemId, int pos, boolean isChecked, int[] viewCoordinates) { + app.logEvent("trip_recording_open"); + MapActivity.clearPrevActivityIntent(); + TripRecordingBottomSheet.showInstance(mapActivity.getSupportFragmentManager()); + return true; + } + }).createItem()); + } + optionsMenuHelper.addItem(new ItemBuilder().setTitleId(R.string.get_directions, mapActivity) .setId(DRAWER_DIRECTIONS_ID) diff --git a/OsmAnd/src/net/osmand/plus/monitoring/ClearRecordedDataBottomSheetFragment.java b/OsmAnd/src/net/osmand/plus/monitoring/ClearRecordedDataBottomSheetFragment.java new file mode 100644 index 0000000000..971bfd01e0 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/monitoring/ClearRecordedDataBottomSheetFragment.java @@ -0,0 +1,160 @@ +package net.osmand.plus.monitoring; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.base.MenuBottomSheetDialogFragment; +import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; +import net.osmand.plus.widgets.TextViewEx; + +import androidx.annotation.DimenRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; + +public class ClearRecordedDataBottomSheetFragment extends MenuBottomSheetDialogFragment implements View.OnClickListener { + + public static final String TAG = ClearRecordedDataBottomSheetFragment.class.getSimpleName(); + + private OsmandApplication app; + + @Override + public void createMenuItems(Bundle savedInstanceState) { + app = requiredMyApplication(); + + items.add(new BottomSheetItemWithDescription.Builder() + .setDescription(app.getString(R.string.clear_recorded_data_warning)) + .setDescriptionColorId(!nightMode ? R.color.text_color_primary_light : R.color.text_color_primary_dark) + .setDescriptionMaxLines(2) + .setTitle(app.getString(R.string.clear_recorded_data)) + .setLayoutId(R.layout.bottom_sheet_item_title_with_description) + .create()); + + LayoutInflater inflater = UiUtilities.getInflater(getContext(), nightMode); + + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(setupBtn(inflater, ButtonType.CLEAR)) + .setOnClickListener(this) + .create()); + + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(setupBtn(inflater, ButtonType.CANCEL)) + .setOnClickListener(this) + .create()); + + } + + private View setupBtn(LayoutInflater inflater, ButtonType type) { + View button = inflater.inflate(R.layout.bottom_sheet_item_button_with_icon, null); + button.setTag(type); + Context context = button.getContext(); + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + int horizontal = context.getResources().getDimensionPixelSize(R.dimen.content_padding); + int top = context.getResources().getDimensionPixelSize(type.topMarginRes); + int bottom = context.getResources().getDimensionPixelSize(R.dimen.content_padding_small); + params.setMargins(horizontal, top, horizontal, bottom); + button.setLayoutParams(params); + + UiUtilities.setupDialogButton(nightMode, button, type.effect, type.titleId); + + TextViewEx title = button.findViewById(R.id.button_text); + int margin = context.getResources().getDimensionPixelSize(R.dimen.context_menu_padding_margin_medium); + UiUtilities.setMargins(title, 0, margin, 0, margin); + + int colorRes; + if (type.effect == UiUtilities.DialogButtonType.SECONDARY_HARMFUL) { + colorRes = R.color.color_osm_edit_delete; + } else { + colorRes = nightMode ? R.color.dlg_btn_secondary_text_dark : R.color.dlg_btn_secondary_text_light; + } + AppCompatImageView icon = button.findViewById(R.id.icon); + icon.setImageDrawable(getIcon(type.iconRes, colorRes)); + + return button; + } + + @Override + public void onClick(View v) { + Object o = v.getTag(); + if (!(o instanceof ButtonType)) { + return; + } + + ButtonType tag = (ButtonType) o; + if (tag == ButtonType.CLEAR) { + app.getSavingTrackHelper().clearRecordedData(true); + } + dismiss(); + } + + @Override + public void onResume() { + super.onResume(); + // Replace later with tTripRecordingActiveBottomSheet.hide() + Fragment target = getTargetFragment(); + if (target instanceof TripRecordingActiveBottomSheet) { + Dialog dialog = ((TripRecordingActiveBottomSheet) target).getDialog(); + if (dialog != null) { + dialog.hide(); + } + } + } + + @Override + public void onPause() { + super.onPause(); + // Replace later with tTripRecordingActiveBottomSheet.show() + Fragment target = getTargetFragment(); + if (target instanceof TripRecordingActiveBottomSheet) { + Dialog dialog = ((TripRecordingActiveBottomSheet) target).getDialog(); + if (dialog != null) { + dialog.show(); + } + } + } + + enum ButtonType { + CLEAR(R.string.clear_recorded_data, R.drawable.ic_action_delete_dark, R.dimen.dialog_content_margin, UiUtilities.DialogButtonType.SECONDARY_HARMFUL), + CANCEL(R.string.shared_string_cancel, R.drawable.ic_action_close, R.dimen.content_padding_small, UiUtilities.DialogButtonType.SECONDARY); + + @StringRes + private final int titleId; + @DrawableRes + private final int iconRes; + @DimenRes + private final int topMarginRes; + private final UiUtilities.DialogButtonType effect; + + ButtonType(int titleId, int iconRes, int topMarginRes, UiUtilities.DialogButtonType effect) { + this.titleId = titleId; + this.iconRes = iconRes; + this.topMarginRes = topMarginRes; + this.effect = effect; + } + } + + @Override + protected boolean hideButtonsContainer() { + return true; + } + + public static void showInstance(@NonNull FragmentManager fragmentManager, @NonNull Fragment target) { + if (!fragmentManager.isStateSaved()) { + ClearRecordedDataBottomSheetFragment fragment = new ClearRecordedDataBottomSheetFragment(); + fragment.setTargetFragment(target, 0); + fragment.show(fragmentManager, TAG); + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java b/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java index 48df51a351..65538733b0 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/OsmandMonitoringPlugin.java @@ -28,7 +28,6 @@ import com.google.android.material.slider.Slider; import net.osmand.AndroidUtils; import net.osmand.Location; import net.osmand.ValueHolder; -import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.NavigationService; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmAndLocationProvider; @@ -44,6 +43,7 @@ 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.SettingsScreenType; +import net.osmand.plus.track.TrackDisplayHelper; import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapTileView; import net.osmand.plus.views.layers.MapInfoLayer; @@ -163,9 +163,9 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { } } - public static final int[] SECONDS = new int[]{0, 1, 2, 3, 5, 10, 15, 20, 30, 60, 90}; - 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}; + public static final int[] SECONDS = new int[] {0, 1, 2, 3, 5, 10, 15, 20, 30, 60, 90}; + 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 SettingsScreenType getSettingsScreenType() { @@ -183,10 +183,9 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { private TextInfoWidget createMonitoringControl(final MapActivity map) { monitoringControl = new TextInfoWidget(map) { long lastUpdateTime; - @Override public boolean updateInfo(DrawSettings drawSettings) { - if (isSaving) { + if(isSaving){ setText(map.getString(R.string.shared_string_save), ""); setIcons(R.drawable.widget_monitoring_rec_big_day, R.drawable.widget_monitoring_rec_big_night); return true; @@ -214,7 +213,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { } final boolean liveMonitoringEnabled = liveMonitoringHelper.isLiveMonitoringEnabled(); - if (globalRecord) { + if(globalRecord) { //indicates global recording (+background recording) if (liveMonitoringEnabled) { dn = R.drawable.widget_live_monitoring_rec_big_night; @@ -320,18 +319,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { } public void controlDialog(final Activity activity, final boolean showTrackSelection) { - - SavingTrackHelper helper = app.getSavingTrackHelper(); - SelectedGpxFile selectedGpxFile = helper.getCurrentTrack(); - boolean hasDataToSave = helper.hasDataToSave(); - boolean wasTrackMonitored = settings.SAVE_GLOBAL_TRACK_TO_GPX.get(); - OsmAndLocationProvider locationProvider = app.getLocationProvider(); - Location lastKnownLocation = locationProvider.getLastKnownLocation(); - FragmentActivity fragmentActivity = (FragmentActivity) activity; - TripRecordingActiveBottomSheet.showInstance(fragmentActivity.getSupportFragmentManager(), - selectedGpxFile, wasTrackMonitored, hasDataToSave, lastKnownLocation == null); - - /*final boolean wasTrackMonitored = settings.SAVE_GLOBAL_TRACK_TO_GPX.get(); + final boolean wasTrackMonitored = settings.SAVE_GLOBAL_TRACK_TO_GPX.get(); final boolean nightMode; if (activity instanceof MapActivity) { nightMode = app.getDaynightHelper().isNightModeForMapControls(); @@ -340,6 +328,8 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { } AlertDialog.Builder bld = new AlertDialog.Builder(UiUtilities.getThemedContext(activity, nightMode)); final TIntArrayList items = new TIntArrayList(); + FragmentActivity fragmentActivity = (FragmentActivity) activity; + TripRecordingActiveBottomSheet.showInstance(fragmentActivity.getSupportFragmentManager()); if (wasTrackMonitored) { items.add(R.string.gpx_monitoring_stop); items.add(R.string.gpx_start_new_segment); @@ -428,8 +418,8 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { run.run(); } }); - bld.show(); - }*/ +// bld.show(); + } } public void saveCurrentTrack() { @@ -491,7 +481,7 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { } } - public void stopRecording() { + public void stopRecording(){ settings.SAVE_GLOBAL_TRACK_TO_GPX.set(false); if (app.getNavigationService() != null) { app.getNavigationService().stopIfNeeded(app, NavigationService.USED_BY_GPX); @@ -580,11 +570,11 @@ public class OsmandMonitoringPlugin extends OsmandPlugin { public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) { String s; int progress = (int) value; - if (progress == 0) { + if(progress == 0) { s = uiCtx.getString(R.string.int_continuosly); v.value = 0; } else { - if (progress < secondsLength) { + if(progress < secondsLength) { s = seconds[progress] + " " + uiCtx.getString(R.string.int_seconds); v.value = seconds[progress] * 1000; } else { diff --git a/OsmAnd/src/net/osmand/plus/monitoring/StopTrackRecordingBottomFragment.java b/OsmAnd/src/net/osmand/plus/monitoring/StopTrackRecordingBottomFragment.java new file mode 100644 index 0000000000..b7bf829457 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/monitoring/StopTrackRecordingBottomFragment.java @@ -0,0 +1,187 @@ +package net.osmand.plus.monitoring; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.OsmandPlugin; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.UiUtilities.DialogButtonType; +import net.osmand.plus.base.MenuBottomSheetDialogFragment; +import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.widgets.TextViewEx; + +import androidx.annotation.DimenRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.appcompat.widget.AppCompatImageView; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; + +public class StopTrackRecordingBottomFragment extends MenuBottomSheetDialogFragment implements View.OnClickListener { + + public static final String TAG = StopTrackRecordingBottomFragment.class.getSimpleName(); + + private OsmandApplication app; + private OsmandSettings settings; + private OsmandMonitoringPlugin plugin; + + @Override + public void createMenuItems(Bundle savedInstanceState) { + app = requiredMyApplication(); + settings = app.getSettings(); + plugin = OsmandPlugin.getPlugin(OsmandMonitoringPlugin.class); + + items.add(new BottomSheetItemWithDescription.Builder() + .setDescription(app.getString(R.string.track_recording_description)) + .setDescriptionColorId(!nightMode ? R.color.text_color_primary_light : R.color.text_color_primary_dark) + .setDescriptionMaxLines(4) + .setTitle(app.getString(R.string.track_recording_title)) + .setLayoutId(R.layout.bottom_sheet_item_title_with_description) + .create()); + + LayoutInflater inflater = UiUtilities.getInflater(getContext(), nightMode); + + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(setupButton(inflater, ButtonType.STOP_AND_DISCARD)) + .setOnClickListener(this) + .create()); + + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(setupButton(inflater, ButtonType.SAVE_AND_STOP)) + .setOnClickListener(this) + .create()); + + items.add(new BaseBottomSheetItem.Builder() + .setCustomView(setupButton(inflater, ButtonType.CANCEL)) + .setOnClickListener(this) + .create()); + } + + private View setupButton(LayoutInflater inflater, ButtonType type) { + View button = inflater.inflate(R.layout.bottom_sheet_item_button_with_icon, null); + button.setTag(type); + Context context = button.getContext(); + + LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + int horizontal = context.getResources().getDimensionPixelSize(R.dimen.content_padding); + int top = context.getResources().getDimensionPixelSize(type.topMarginRes); + int bottom = context.getResources().getDimensionPixelSize(R.dimen.content_padding_small); + params.setMargins(horizontal, top, horizontal, bottom); + button.setLayoutParams(params); + + UiUtilities.setupDialogButton(nightMode, button, type.effect, type.titleId); + + TextViewEx title = button.findViewById(R.id.button_text); + int margin = context.getResources().getDimensionPixelSize(R.dimen.context_menu_padding_margin_medium); + UiUtilities.setMargins(title, 0, margin, 0, margin); + + int colorRes; + if (type.effect == DialogButtonType.SECONDARY_HARMFUL) { + colorRes = R.color.color_osm_edit_delete; + } else { + colorRes = nightMode ? R.color.dlg_btn_secondary_text_dark : R.color.dlg_btn_secondary_text_light; + } + AppCompatImageView icon = button.findViewById(R.id.icon); + icon.setImageDrawable(getIcon(type.iconRes, colorRes)); + + if (type == ButtonType.STOP_AND_DISCARD) { + int size = context.getResources().getDimensionPixelSize(R.dimen.map_widget_height); + LinearLayout.LayoutParams iconParams = new LinearLayout.LayoutParams(size, size); + icon.setLayoutParams(iconParams); + } + + return button; + } + + @Override + public void onClick(View v) { + Object o = v.getTag(); + if (!(o instanceof ButtonType)) { + return; + } + + ButtonType tag = (ButtonType) o; + if (tag == ButtonType.STOP_AND_DISCARD) { + if (plugin != null && settings.SAVE_GLOBAL_TRACK_TO_GPX.get()) { + plugin.stopRecording(); + app.getNotificationHelper().refreshNotifications(); + } + app.getSavingTrackHelper().clearRecordedData(true); + } else if (tag == ButtonType.SAVE_AND_STOP) { + if (plugin != null && settings.SAVE_GLOBAL_TRACK_TO_GPX.get()) { + plugin.saveCurrentTrack(); + app.getNotificationHelper().refreshNotifications(); + } + } + dismiss(); + } + + @Override + public void onResume() { + super.onResume(); + // Replace later with tTripRecordingActiveBottomSheet.hide() + Fragment target = getTargetFragment(); + if (target instanceof TripRecordingActiveBottomSheet) { + Dialog dialog = ((TripRecordingActiveBottomSheet) target).getDialog(); + if (dialog != null) { + dialog.hide(); + } + } + } + + @Override + public void onPause() { + super.onPause(); + // Replace later with tTripRecordingActiveBottomSheet.show() + Fragment target = getTargetFragment(); + if (target instanceof TripRecordingActiveBottomSheet) { + Dialog dialog = ((TripRecordingActiveBottomSheet) target).getDialog(); + if (dialog != null) { + dialog.show(); + } + } + } + + enum ButtonType { + STOP_AND_DISCARD(R.string.track_recording_stop_without_saving, R.drawable.ic_action_rec_stop, R.dimen.dialog_content_margin, DialogButtonType.SECONDARY_HARMFUL), + SAVE_AND_STOP(R.string.track_recording_save_and_stop, R.drawable.ic_action_save_to_file, R.dimen.content_padding_small, DialogButtonType.SECONDARY), + CANCEL(R.string.shared_string_cancel, R.drawable.ic_action_close, R.dimen.zero, DialogButtonType.SECONDARY); + + @StringRes + private final int titleId; + @DrawableRes + private final int iconRes; + @DimenRes + private final int topMarginRes; + private final DialogButtonType effect; + + ButtonType(int titleId, int iconRes, int topMarginRes, DialogButtonType type) { + this.titleId = titleId; + this.iconRes = iconRes; + this.topMarginRes = topMarginRes; + this.effect = type; + } + } + + @Override + protected boolean hideButtonsContainer() { + return true; + } + + public static void showInstance(@NonNull FragmentManager fragmentManager, @NonNull Fragment target) { + if (!fragmentManager.isStateSaved()) { + StopTrackRecordingBottomFragment fragment = new StopTrackRecordingBottomFragment(); + fragment.setTargetFragment(target, 0); + fragment.show(fragmentManager, TAG); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingActiveBottomSheet.java b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingActiveBottomSheet.java index 5f5c662882..23bf24e287 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingActiveBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingActiveBottomSheet.java @@ -6,13 +6,17 @@ import android.app.Dialog; import android.content.Context; import android.graphics.Typeface; import android.graphics.drawable.Drawable; +import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.TypedValue; +import android.text.format.DateUtils; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; +import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; @@ -25,10 +29,15 @@ import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.AppCompatImageView; 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 androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.snackbar.Snackbar; + import net.osmand.AndroidUtils; +import net.osmand.GPXUtilities; import net.osmand.Location; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmAndLocationProvider; @@ -36,16 +45,25 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; +import net.osmand.plus.myplaces.SaveCurrentTrackTask; import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.track.GpxBlockStatisticsBuilder; +import net.osmand.plus.track.SaveGpxAsyncTask; import net.osmand.plus.track.TrackAppearanceFragment; import net.osmand.plus.widgets.TextViewEx; import net.osmand.util.Algorithms; +import java.lang.ref.WeakReference; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + import static net.osmand.plus.UiUtilities.CompoundButtonType.PROFILE_DEPENDENT; public class TripRecordingActiveBottomSheet extends MenuBottomSheetDialogFragment { @@ -65,6 +83,44 @@ public class TripRecordingActiveBottomSheet extends MenuBottomSheetDialogFragmen private final Handler handler = new Handler(); private Runnable updatingGPS; + SaveGpxAsyncTask.SaveGpxListener saveGpxListener = new SaveGpxAsyncTask.SaveGpxListener() { + + @Override + public void gpxSavingStarted() { + + } + + @Override + public void gpxSavingFinished(Exception errorMessage) { + String gpxFileName = Algorithms.getFileWithoutDirs(app.getSavingTrackHelper().getCurrentTrack().getGpxFile().path); + final MapActivity mapActivity = getMapActivity(); + final Context context = getContext(); + SavingTrackHelper helper = app.getSavingTrackHelper(); + final SavingTrackHelper.SaveGpxResult result = helper.saveDataToGpx(app.getAppCustomization().getTracksDir()); + if (mapActivity != null && context != null) { + Snackbar snackbar = Snackbar.make(mapActivity.getLayout(), + getString(R.string.shared_string_file_is_saved, gpxFileName), + Snackbar.LENGTH_LONG) + .setAction(R.string.shared_string_undo, new View.OnClickListener() { + @Override + public void onClick(View view) { + final WeakReference mapActivityRef = new WeakReference<>(mapActivity); + final FragmentActivity fragmentActivity = mapActivityRef.get(); + SaveGPXBottomSheetFragment.showInstance(fragmentActivity.getSupportFragmentManager(), result.getFilenames()); + } + }); + View view = snackbar.getView(); + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) view.getLayoutParams(); + params.gravity = Gravity.TOP; + AndroidUtils.setMargins(params, 0, AndroidUtils.getStatusBarHeight(context), 0, 0); + view.setLayoutParams(params); + UiUtilities.setupSnackbar(snackbar, nightMode); + snackbar.show(); + } + } + + }; + public void setSelectedGpxFile(SelectedGpxFile selectedGpxFile) { this.selectedGpxFile = selectedGpxFile; } @@ -81,17 +137,46 @@ public class TripRecordingActiveBottomSheet extends MenuBottomSheetDialogFragmen this.searchingGPS = searchingGPS; } + public static void showInstance(@NonNull FragmentManager fragmentManager, SelectedGpxFile selectedGpxFile, + boolean wasTrackMonitored, boolean hasDataToSave, boolean searchingGPS) { + if (!fragmentManager.isStateSaved()) { + TripRecordingActiveBottomSheet fragment = new TripRecordingActiveBottomSheet(); + fragment.setSelectedGpxFile(selectedGpxFile); + fragment.setWasTrackMonitored(wasTrackMonitored); + fragment.setHasDataToSave(hasDataToSave); + fragment.setSearchingGPS(searchingGPS); + fragment.show(fragmentManager, TAG); + } + } + @Override public void createMenuItems(Bundle savedInstanceState) { app = requiredMyApplication(); settings = app.getSettings(); LayoutInflater inflater = UiUtilities.getInflater(getContext(), nightMode); + final FragmentManager fragmentManager = getFragmentManager(); + final Fragment targetFragment = getTargetFragment(); View itemView = inflater.inflate(R.layout.trip_recording_active_fragment, null, false); items.add(new BottomSheetItemWithDescription.Builder() .setCustomView(itemView) .create()); + long timeTrackSaved = app.getSavingTrackHelper().getLastTimeUpdated(); + SimpleDateFormat sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss:SSS Z"); + sdf.setTimeZone(TimeZone.getTimeZone("GMT")); + Date resultDate = new Date(timeTrackSaved); + String sdfFormatted = sdf.format(resultDate); + CharSequence formattedTimeTrackSaved = null; + try { + long time = sdf.parse(sdfFormatted).getTime(); + long now = System.currentTimeMillis(); + formattedTimeTrackSaved = + DateUtils.getRelativeTimeSpanString(time, now, DateUtils.MINUTE_IN_MILLIS); + } catch (ParseException e) { + e.printStackTrace(); + } + View buttonClear = itemView.findViewById(R.id.button_clear); View buttonStart = itemView.findViewById(R.id.button_start); View buttonSave = itemView.findViewById(R.id.button_save); @@ -100,7 +185,7 @@ public class TripRecordingActiveBottomSheet extends MenuBottomSheetDialogFragmen createItem(buttonClear, ItemType.CLEAR_DATA, hasDataToSave, null); createItem(buttonStart, ItemType.START_SEGMENT, wasTrackMonitored, null); - createItem(buttonSave, ItemType.SAVE, hasDataToSave, "..."); + createItem(buttonSave, ItemType.SAVE, hasDataToSave, (String) formattedTimeTrackSaved); createItem(buttonPause, wasTrackMonitored ? ItemType.PAUSE : ItemType.RESUME, true, null); createItem(buttonStop, ItemType.STOP, true, null); @@ -179,6 +264,32 @@ public class TripRecordingActiveBottomSheet extends MenuBottomSheetDialogFragmen createItem(additionalButton, ItemType.APPEARANCE, checked, null); } }); + + buttonSave.findViewById(R.id.button_container).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final GPXUtilities.GPXFile gpxFile = app.getSavingTrackHelper().getCurrentTrack().getGpxFile(); + new SaveCurrentTrackTask(app, gpxFile, saveGpxListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + + buttonStop.findViewById(R.id.button_container).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (fragmentManager != null) { + StopTrackRecordingBottomFragment.showInstance(fragmentManager, targetFragment); + } + } + }); + + buttonClear.findViewById(R.id.button_container).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (fragmentManager != null) { + ClearRecordedDataBottomSheetFragment.showInstance(fragmentManager, targetFragment); + } + } + }); } private void updateStatus() { @@ -421,16 +532,4 @@ public class TripRecordingActiveBottomSheet extends MenuBottomSheetDialogFragmen protected boolean useVerticalButtons() { return true; } - - public static void showInstance(@NonNull FragmentManager fragmentManager, SelectedGpxFile selectedGpxFile, - boolean wasTrackMonitored, boolean hasDataToSave, boolean searchingGPS) { - if (!fragmentManager.isStateSaved()) { - TripRecordingActiveBottomSheet fragment = new TripRecordingActiveBottomSheet(); - fragment.setSelectedGpxFile(selectedGpxFile); - fragment.setWasTrackMonitored(wasTrackMonitored); - fragment.setHasDataToSave(hasDataToSave); - fragment.setSearchingGPS(searchingGPS); - fragment.show(fragmentManager, TAG); - } - } }