From 5e6a34e24ee92801e5c19f867853e072adde1ed9 Mon Sep 17 00:00:00 2001 From: Chumva Date: Wed, 24 Jul 2019 15:19:17 +0300 Subject: [PATCH] Add set time dialog and improve userGpxInfo fragment --- .../res/layout/bottom_sheet_set_time.xml | 226 +++++ .../res/layout/fragment_user_gpx_info.xml | 880 +++++++++--------- OsmAnd-telegram/res/values/dimens.xml | 6 +- OsmAnd-telegram/res/values/strings.xml | 5 + .../osmand/telegram/ui/SetTimeBottomSheet.kt | 186 ++++ .../osmand/telegram/ui/UserGpxInfoFragment.kt | 302 +++--- .../osmand/telegram/utils/OsmandFormatter.kt | 4 +- 7 files changed, 1009 insertions(+), 600 deletions(-) create mode 100644 OsmAnd-telegram/res/layout/bottom_sheet_set_time.xml create mode 100644 OsmAnd-telegram/src/net/osmand/telegram/ui/SetTimeBottomSheet.kt diff --git a/OsmAnd-telegram/res/layout/bottom_sheet_set_time.xml b/OsmAnd-telegram/res/layout/bottom_sheet_set_time.xml new file mode 100644 index 0000000000..0bc539b5c7 --- /dev/null +++ b/OsmAnd-telegram/res/layout/bottom_sheet_set_time.xml @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml b/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml index 205496fddf..3a1b3d71be 100644 --- a/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml +++ b/OsmAnd-telegram/res/layout/fragment_user_gpx_info.xml @@ -11,358 +11,411 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:paddingBottom="@dimen/content_padding_standard" + app:layout_scrollFlags="scroll"> + + + android:orientation="vertical"> + + - - - - - - - - + android:layout_gravity="bottom|center_horizontal" + android:ellipsize="end" + android:gravity="center" + android:maxLines="1" + android:text="@string/start_location_sharing" + android:textColor="?android:textColorPrimary" + android:textSize="@dimen/title_text_size" + app:typeface="@string/font_roboto_mono_bold" /> - + - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + android:paddingLeft="@dimen/content_padding_standard" + android:paddingRight="@dimen/content_padding_standard"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + android:paddingLeft="@dimen/content_padding_standard" + android:paddingRight="@dimen/content_padding_standard"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -370,175 +423,90 @@ + android:layout_marginLeft="@dimen/content_padding_standard" + android:layout_marginTop="@dimen/list_header_height" + android:layout_marginRight="@dimen/content_padding_standard" + android:layout_marginBottom="@dimen/image_button_padding" + android:baselineAligned="false" + android:orientation="horizontal"> + android:background="@drawable/btn_round" + android:gravity="center_vertical"> + android:id="@+id/open_in_osmand_icon" + android:layout_width="@dimen/list_item_icon_size" + android:layout_height="@dimen/list_item_icon_size" + android:layout_marginStart="@dimen/image_button_padding" + android:layout_marginLeft="@dimen/image_button_padding" + android:layout_marginTop="@dimen/image_button_padding" + android:layout_marginBottom="@dimen/image_button_padding" + android:src="@drawable/ic_logo_osmand_free" /> - - - - - - - + + android:background="@drawable/btn_round" + android:gravity="center_vertical"> + android:id="@+id/share_gpx_icon" + android:layout_width="@dimen/list_item_icon_size" + android:layout_height="@dimen/list_item_icon_size" + android:layout_marginStart="@dimen/image_button_padding" + android:layout_marginLeft="@dimen/image_button_padding" + tools:src="@drawable/ic_action_share" + tools:tint="@color/ctrl_active_light" /> - - - - - - - + - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/OsmAnd-telegram/res/values/dimens.xml b/OsmAnd-telegram/res/values/dimens.xml index 97f34a64b3..e2240cf521 100644 --- a/OsmAnd-telegram/res/values/dimens.xml +++ b/OsmAnd-telegram/res/values/dimens.xml @@ -1,5 +1,6 @@ + 2dp 4dp 8dp 16dp @@ -54,7 +55,7 @@ 32dp 60dp - 56dp + 84dp 50dp 132dp @@ -76,6 +77,8 @@ 60dp + 18dp + 112dp 22sp @@ -88,6 +91,7 @@ 16sp 13sp + 12sp 15sp diff --git a/OsmAnd-telegram/res/values/strings.xml b/OsmAnd-telegram/res/values/strings.xml index 0392d839e2..f3411f6a03 100644 --- a/OsmAnd-telegram/res/values/strings.xml +++ b/OsmAnd-telegram/res/values/strings.xml @@ -1,5 +1,10 @@ + End + Start + Apply + Select time to display + Start — End date We don`t have collected data for the selected day No data Select diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/SetTimeBottomSheet.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/SetTimeBottomSheet.kt new file mode 100644 index 0000000000..6c603ebd0e --- /dev/null +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/SetTimeBottomSheet.kt @@ -0,0 +1,186 @@ +package net.osmand.telegram.ui + +import android.app.DatePickerDialog +import android.app.TimePickerDialog +import android.content.Intent +import android.os.Bundle +import android.support.design.widget.BottomSheetBehavior +import android.support.v4.app.DialogFragment +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import net.osmand.telegram.R +import net.osmand.telegram.TelegramApplication +import net.osmand.telegram.ui.views.BottomSheetDialog +import net.osmand.telegram.utils.OsmandFormatter +import java.util.* + +class SetTimeBottomSheet : DialogFragment() { + + private val app: TelegramApplication + get() = activity?.application as TelegramApplication + + private lateinit var dateStartBtn: TextView + private lateinit var timeStartBtn: TextView + private lateinit var dateEndBtn: TextView + private lateinit var timeEndBtn: TextView + + private var startCalendar = Calendar.getInstance() + private var endCalendar = Calendar.getInstance() + + override fun onCreateDialog(savedInstanceState: Bundle?) = BottomSheetDialog(context!!) + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val mainView = inflater.inflate(R.layout.bottom_sheet_set_time, container, false) + + readFromBundle(savedInstanceState ?: arguments) + + mainView.findViewById(R.id.scroll_view_container).setOnClickListener { dismiss() } + + BottomSheetBehavior.from(mainView.findViewById(R.id.scroll_view)) + .setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() { + + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == BottomSheetBehavior.STATE_HIDDEN) { + dismiss() + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) {} + }) + + dateStartBtn = mainView.findViewById(R.id.date_start_btn).apply { + setOnClickListener { selectStartDate() } + } + timeStartBtn = mainView.findViewById(R.id.time_start_btn).apply { + setOnClickListener { selectStartTime() } + } + dateEndBtn = mainView.findViewById(R.id.date_end_btn).apply { + setOnClickListener { selectEndDate() } + } + timeEndBtn = mainView.findViewById(R.id.time_end_btn).apply { + setOnClickListener { selectEndTime() } + } + updateDateAndTimeButtons() + + mainView.findViewById(R.id.secondary_btn).apply { + setText(R.string.shared_string_cancel) + setOnClickListener { dismiss() } + } + + mainView.findViewById(R.id.primary_btn).apply { + setText(R.string.shared_string_apply) + setOnClickListener { + targetFragment?.also { target -> + val intent = Intent().apply { + putExtra(START_TIME, startCalendar.timeInMillis) + putExtra(END_TIME, endCalendar.timeInMillis) + } + target.onActivityResult(targetRequestCode, SET_TIME_REQUEST_CODE, intent) + } + dismiss() + } + } + + return mainView + } + + private fun readFromBundle(bundle: Bundle?) { + bundle?.also { + startCalendar.timeInMillis = it.getLong(START_TIME) + endCalendar.timeInMillis = it.getLong(END_TIME) + } + } + + private fun updateDateAndTimeButtons() { + dateStartBtn.text = OsmandFormatter.getFormattedDate(startCalendar.timeInMillis / 1000, false) + dateEndBtn.text = OsmandFormatter.getFormattedDate(endCalendar.timeInMillis / 1000, false) + + timeStartBtn.text = OsmandFormatter.getFormattedTime(startCalendar.timeInMillis, useCurrentTime = false, short = true) + timeEndBtn.text = OsmandFormatter.getFormattedTime(endCalendar.timeInMillis, useCurrentTime = false, short = true) + } + + private fun selectStartDate() { + val dateFromDialog = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> + startCalendar.set(Calendar.YEAR, year) + startCalendar.set(Calendar.MONTH, monthOfYear) + startCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + updateDateAndTimeButtons() + } + DatePickerDialog( + context, dateFromDialog, + startCalendar.get(Calendar.YEAR), + startCalendar.get(Calendar.MONTH), + startCalendar.get(Calendar.DAY_OF_MONTH) + ).show() + } + + private fun selectStartTime() { + TimePickerDialog( + context, + TimePickerDialog.OnTimeSetListener { _, hours, minutes -> + startCalendar.set(Calendar.HOUR_OF_DAY, hours) + startCalendar.set(Calendar.MINUTE, minutes) + updateDateAndTimeButtons() + }, 0, 0, true + ).show() + } + + private fun selectEndDate() { + val dateFromDialog = DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> + endCalendar.set(Calendar.YEAR, year) + endCalendar.set(Calendar.MONTH, monthOfYear) + endCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + updateDateAndTimeButtons() + } + DatePickerDialog( + context, dateFromDialog, + endCalendar.get(Calendar.YEAR), + endCalendar.get(Calendar.MONTH), + endCalendar.get(Calendar.DAY_OF_MONTH) + ).show() + } + + private fun selectEndTime() { + TimePickerDialog( + context, + TimePickerDialog.OnTimeSetListener { _, hours, minutes -> + endCalendar.set(Calendar.HOUR_OF_DAY, hours) + endCalendar.set(Calendar.MINUTE, minutes) + updateDateAndTimeButtons() + }, 0, 0, true + ).show() + } + + companion object { + + const val SET_TIME_REQUEST_CODE = 2 + const val START_TIME = "start_time" + const val END_TIME = "end_time" + + private const val TAG = "SetTimeBottomSheet" + + fun showInstance(fm: FragmentManager, target: Fragment, start: Long, end: Long): Boolean { + return try { + SetTimeBottomSheet().apply { + arguments = Bundle().apply { + putLong(START_TIME, start) + putLong(END_TIME, end) + } + setTargetFragment(target, SET_TIME_REQUEST_CODE) + show(fm, TAG) + } + true + } catch (e: RuntimeException) { + false + } + } + } +} \ No newline at end of file diff --git a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt index b37cbdfa8f..02b23f0b6b 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/ui/UserGpxInfoFragment.kt @@ -1,19 +1,18 @@ package net.osmand.telegram.ui -import android.app.DatePickerDialog -import android.app.TimePickerDialog import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable -import android.os.Build -import android.os.Bundle -import android.os.Handler -import android.os.Message +import android.os.* import android.support.design.widget.Snackbar import android.support.v4.app.FragmentManager +import android.support.v4.content.ContextCompat +import android.text.SpannableString +import android.text.Spanned +import android.text.style.ForegroundColorSpan import android.util.DisplayMetrics import android.view.LayoutInflater import android.view.View @@ -27,8 +26,8 @@ import net.osmand.GPXUtilities import net.osmand.PlatformUtil import net.osmand.aidl.gpx.AGpxBitmap import net.osmand.telegram.R +import net.osmand.telegram.TelegramApplication import net.osmand.telegram.TelegramSettings -import net.osmand.telegram.helpers.LocationMessages import net.osmand.telegram.helpers.OsmandAidlHelper import net.osmand.telegram.helpers.TelegramUiHelper import net.osmand.telegram.utils.AndroidUtils @@ -45,15 +44,17 @@ class UserGpxInfoFragment : BaseDialogFragment() { private val uiUtils get() = app.uiUtils - private var gpxFile = GPXUtilities.GPXFile("") + private var gpxDataItem: GpxDataItem? = null + + private var loadGpxAsyncTask: LoadGpxAsyncTask? = null + private lateinit var loadGpxListener: LoadGpxListener private lateinit var mainView: View - private lateinit var dateStartBtn: TextView - private lateinit var timeStartBtn: TextView - private lateinit var dateEndBtn: TextView - private lateinit var timeEndBtn: TextView + private lateinit var dateTimeBtn: TextView private lateinit var liveBtn: TextView + private lateinit var iconMap: ImageView + private lateinit var avgElevationTv: TextView private lateinit var avgSpeedTv: TextView private lateinit var totalDistanceTv: TextView @@ -62,8 +63,6 @@ class UserGpxInfoFragment : BaseDialogFragment() { private var startCalendar = Calendar.getInstance() private var endCalendar = Calendar.getInstance() - private var locationMessages = emptyList() - private var userId = -1 private var chatId = -1L private var deviceName = "" @@ -92,25 +91,42 @@ class UserGpxInfoFragment : BaseDialogFragment() { TelegramUiHelper.setupPhoto(app, mainView.findViewById(R.id.user_icon), telegramHelper.getUserPhotoPath(user), R.drawable.img_user_placeholder, false) } - val openGpxListener = View.OnClickListener { - val gpx = gpxFile - if (gpx.path.isNotEmpty()) { - openGpx(gpx.path) - } else { - saveCurrentGpxToFile(object : - OsmandLocationUtils.SaveGpxListener { - override fun onSavingGpxFinish(path: String) { - openGpx(path) - } + loadGpxListener = object : LoadGpxListener { + override fun onLoadGpxFinish(dataItem: GpxDataItem) { + gpxDataItem = dataItem + updateGPXStatisticRow() + updateDateAndTimeButtons() + updateGPXMap() + } - override fun onSavingGpxError(error: Exception) { - Toast.makeText(app, error.message, Toast.LENGTH_LONG).show() - } - }) + override fun onLoadGpxError(error: String) { + log.error(error) } } - val iconMap = mainView.findViewById(R.id.gpx_map) + + val openGpxListener = View.OnClickListener { + val gpx = gpxDataItem?.gpxFile + if (gpx != null) { + if (gpx.path.isNotEmpty()) { + openGpx(gpx.path) + } else { + saveCurrentGpxToFile(object : + OsmandLocationUtils.SaveGpxListener { + + override fun onSavingGpxFinish(path: String) { + openGpx(path) + } + + override fun onSavingGpxError(error: Exception) { + Toast.makeText(app, error.message, Toast.LENGTH_LONG).show() + } + }) + } + } + } + + iconMap = mainView.findViewById(R.id.gpx_map) app.osmandAidlHelper.setGpxBitmapCreatedListener( object : OsmandAidlHelper.GpxBitmapCreatedListener { override fun onGpxBitmapCreated(bitmap: AGpxBitmap) { @@ -128,10 +144,14 @@ class UserGpxInfoFragment : BaseDialogFragment() { } } - dateStartBtn = mainView.findViewById(R.id.date_start_btn) - timeStartBtn = mainView.findViewById(R.id.time_start_btn) - dateEndBtn = mainView.findViewById(R.id.date_end_btn) - timeEndBtn = mainView.findViewById(R.id.time_end_btn) + dateTimeBtn = mainView.findViewById(R.id.date_time_btn).apply { + setOnClickListener { + fragmentManager?.also { fm -> + SetTimeBottomSheet.showInstance(fm, this@UserGpxInfoFragment, startCalendar.timeInMillis, endCalendar.timeInMillis) + } + } + setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) + } liveBtn = mainView.findViewById(R.id.live_btn).apply { setOnClickListener { @@ -145,36 +165,11 @@ class UserGpxInfoFragment : BaseDialogFragment() { } updateLiveTrackBtn() - dateStartBtn.setOnClickListener { selectStartDate() } - timeStartBtn.setOnClickListener { selectStartTime() } - dateEndBtn.setOnClickListener { selectEndDate() } - timeEndBtn.setOnClickListener { selectEndTime() } - - setupBtnTextColor(dateStartBtn) - setupBtnTextColor(timeStartBtn) - setupBtnTextColor(dateEndBtn) - setupBtnTextColor(timeEndBtn) - - updateDateAndTimeButtons() - avgElevationTv = mainView.findViewById(R.id.average_altitude_text) avgSpeedTv = mainView.findViewById(R.id.average_speed_text) totalDistanceTv = mainView.findViewById(R.id.distance_text) timeSpanTv = mainView.findViewById(R.id.duration_text) - mainView.findViewById(R.id.average_altitude_icon).apply { - setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_altitude_range)) - } - mainView.findViewById(R.id.average_speed_icon).apply { - setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_speed_average)) - } - mainView.findViewById(R.id.distance_icon).apply { - setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_sort_by_distance)) - } - mainView.findViewById(R.id.duration_icon).apply { - setImageDrawable(uiUtils.getThemedIcon(R.drawable.ic_action_time_span)) - } - updateGPXStatisticRow() val imageRes = if (app.isOsmAndInstalled()) { @@ -185,26 +180,28 @@ class UserGpxInfoFragment : BaseDialogFragment() { mainView.findViewById(R.id.open_in_osmand_icon).setImageResource(imageRes) mainView.findViewById(R.id.open_in_osmand_btn).setOnClickListener(openGpxListener) - mainView.findViewById(R.id.open_in_osmand_title).setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.primary_text_light, R.color.ctrl_light)) - mainView.findViewById(R.id.share_gpx_title).setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.primary_text_light, R.color.ctrl_light)) + mainView.findViewById(R.id.open_in_osmand_title).setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) + mainView.findViewById(R.id.share_gpx_title).setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) mainView.findViewById(R.id.share_gpx_icon).setImageDrawable(getShareIcon()) mainView.findViewById(R.id.share_gpx_btn).apply { setOnClickListener { - val gpx = gpxFile - if (gpx.path.isNotEmpty()) { - (activity as MainActivity).shareGpx(gpx.path) - } else { - saveCurrentGpxToFile(object : - OsmandLocationUtils.SaveGpxListener { - override fun onSavingGpxFinish(path: String) { - (activity as MainActivity).shareGpx(path) - } + val gpx = gpxDataItem?.gpxFile + if (gpx != null) { + if (gpx.path.isNotEmpty()) { + (activity as MainActivity).shareGpx(gpx.path) + } else { + saveCurrentGpxToFile(object : + OsmandLocationUtils.SaveGpxListener { + override fun onSavingGpxFinish(path: String) { + (activity as MainActivity).shareGpx(path) + } - override fun onSavingGpxError(error: Exception) { - Toast.makeText(app, error.message, Toast.LENGTH_LONG).show() - } - }) + override fun onSavingGpxError(error: Exception) { + Toast.makeText(app, error.message, Toast.LENGTH_LONG).show() + } + }) + } } } } @@ -216,6 +213,9 @@ class UserGpxInfoFragment : BaseDialogFragment() { override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) + outState.putInt(USER_ID_KEY, userId) + outState.putLong(CHAT_ID_KEY, chatId) + outState.putString(DEVICE_NAME_KEY, deviceName) outState.putLong(START_KEY, startCalendar.timeInMillis) outState.putLong(END_KEY, endCalendar.timeInMillis) } @@ -224,6 +224,18 @@ class UserGpxInfoFragment : BaseDialogFragment() { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { ChooseOsmAndBottomSheet.OSMAND_CHOSEN_REQUEST_CODE -> updateGPXMap() + SetTimeBottomSheet.SET_TIME_REQUEST_CODE -> { + if (data != null) { + val startTime = data.getLongExtra(SetTimeBottomSheet.START_TIME, -1) + val endTime = data.getLongExtra(SetTimeBottomSheet.END_TIME, -1) + if (startTime != -1L && endTime != -1L) { + endTimeChanged = endCalendar.timeInMillis != endTime + startCalendar.timeInMillis = startTime + endCalendar.timeInMillis = endTime + updateGpxInfo() + } + } + } } } @@ -235,10 +247,8 @@ class UserGpxInfoFragment : BaseDialogFragment() { } private fun startHandler() { - log.debug("startHandler") if (!handler.hasMessages(TRACK_UPDATE_MSG_ID)) { val msg = Message.obtain(handler) { - log.debug("Handler postDelayed") if (isResumed && liveTrackEnabled()) { updateGpxInfo() startHandler() @@ -266,7 +276,10 @@ class UserGpxInfoFragment : BaseDialogFragment() { } private fun saveCurrentGpxToFile(listener: OsmandLocationUtils.SaveGpxListener) { - OsmandLocationUtils.saveGpx(app, gpxFile, listener) + val gpx = gpxDataItem?.gpxFile + if (gpx != null) { + OsmandLocationUtils.saveGpx(app, gpx, listener) + } } private fun readFromBundle(bundle: Bundle?) { @@ -281,17 +294,13 @@ class UserGpxInfoFragment : BaseDialogFragment() { private fun liveTrackEnabled() = settings.isLiveTrackEnabled(userId, chatId, deviceName) - private fun setupBtnTextColor(textView: TextView) { - textView.setTextColor(AndroidUtils.createPressedColorStateList(app, true, R.color.ctrl_active_light, R.color.ctrl_light)) - } - private fun updateLiveTrackBtn() { val enabled = liveTrackEnabled() val icon = getLiveTrackBtnIcon(enabled) val normalTextColor = if (enabled) R.color.ctrl_active_light else R.color.secondary_text_light liveBtn.setTextColor(AndroidUtils.createPressedColorStateList(app, true, normalTextColor, R.color.ctrl_light)) - liveBtn.setCompoundDrawablesWithIntrinsicBounds(null, null, icon, null) + liveBtn.setCompoundDrawablesWithIntrinsicBounds(icon, null, null, null) } private fun getShareIcon(): Drawable? { @@ -325,12 +334,16 @@ class UserGpxInfoFragment : BaseDialogFragment() { private fun updateGpxInfo() { checkTime() - locationMessages = app.locationMessages.getMessagesForUserInChat(userId, chatId, deviceName, startCalendar.timeInMillis, endCalendar.timeInMillis) + stopLoadGpxAsyncTask() + loadGpxAsyncTask = LoadGpxAsyncTask(app, userId, chatId, deviceName, startCalendar.timeInMillis, endCalendar.timeInMillis, loadGpxListener) + loadGpxAsyncTask!!.execute() + } - gpxFile = OsmandLocationUtils.convertLocationMessagesToGpxFiles(app, locationMessages).firstOrNull() ?: GPXUtilities.GPXFile(app.packageName) - updateGPXStatisticRow() - updateDateAndTimeButtons() - updateGPXMap() + private fun stopLoadGpxAsyncTask() { + val asyncTask = loadGpxAsyncTask + if (asyncTask != null && asyncTask.status == AsyncTask.Status.RUNNING) { + asyncTask.cancel(false) + } } private fun checkTime() { @@ -349,20 +362,26 @@ class UserGpxInfoFragment : BaseDialogFragment() { } private fun updateDateAndTimeButtons() { - dateStartBtn.text = SimpleDateFormat("dd MMM", Locale.getDefault()).format(startCalendar.timeInMillis) - dateEndBtn.text = SimpleDateFormat("dd MMM", Locale.getDefault()).format(endCalendar.timeInMillis) - - timeStartBtn.text = SimpleDateFormat("HH:mm", Locale.getDefault()).format(startCalendar.timeInMillis) - timeEndBtn.text = SimpleDateFormat("HH:mm", Locale.getDefault()).format(endCalendar.timeInMillis) + val dateFormat = SimpleDateFormat(DATE_FORMAT, Locale.getDefault()) + val start = dateFormat.format(startCalendar.timeInMillis) + val end = dateFormat.format(endCalendar.timeInMillis) + val text = "$start — $end" + dateTimeBtn.text = SpannableString(text).apply { + val index = text.indexOf("—") + if (index != -1) { + setSpan(ForegroundColorSpan(ContextCompat.getColor(app, R.color.secondary_text_light)), index, index + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + } } private fun updateGPXStatisticRow() { - val analysis: GPXUtilities.GPXTrackAnalysis = gpxFile.getAnalysis(0) - - avgElevationTv.text = if (analysis.avgElevation != 0.0) OsmandFormatter.getFormattedAlt(analysis.avgElevation, app) else "-" - avgSpeedTv.text = if (analysis.isSpeedSpecified) OsmandFormatter.getFormattedSpeed(analysis.avgSpeed, app) else "-" - totalDistanceTv.text = if (analysis.totalDistance != 0.0f) OsmandFormatter.getFormattedDistance(analysis.totalDistance, app) else "-" - timeSpanTv.text = if (analysis.timeSpan != 0L) Algorithms.formatDuration((analysis.timeSpan / 1000).toInt(), true) else "-" + val analysis = gpxDataItem?.analysis + if (analysis != null) { + avgElevationTv.text = if (analysis.avgElevation != 0.0) OsmandFormatter.getFormattedAlt(analysis.avgElevation, app) else "-" + avgSpeedTv.text = if (analysis.isSpeedSpecified) OsmandFormatter.getFormattedSpeed(analysis.avgSpeed, app) else "-" + totalDistanceTv.text = if (analysis.totalDistance != 0.0f) OsmandFormatter.getFormattedDistance(analysis.totalDistance, app) else "-" + timeSpanTv.text = if (analysis.timeSpan != 0L) Algorithms.formatDuration((analysis.timeSpan / 1000).toInt(), true) else "-" + } } private fun updateGPXMap() { @@ -388,8 +407,8 @@ class UserGpxInfoFragment : BaseDialogFragment() { if (mgr != null) { val dm = DisplayMetrics() (mgr as WindowManager).defaultDisplay.getMetrics(dm) - val widthPixels = dm.widthPixels - (2 * app.resources.getDimensionPixelSize(R.dimen.content_padding_standard)) - val heightPixels = AndroidUtils.dpToPx(app, 152f) + val widthPixels = iconMap.width + val heightPixels = iconMap.height val gpxUri = AndroidUtils.getUriForFile(app, File(path)) app.osmandAidlHelper.execOsmandApi { app.osmandAidlHelper.getBitmapForGpx(gpxUri, dm.density, widthPixels, heightPixels, GPX_TRACK_COLOR) @@ -404,54 +423,54 @@ class UserGpxInfoFragment : BaseDialogFragment() { } } - private fun selectStartDate() { - val dateFromDialog = - DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> - startCalendar.set(Calendar.YEAR, year) - startCalendar.set(Calendar.MONTH, monthOfYear) - startCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) - updateGpxInfo() + private class LoadGpxAsyncTask internal constructor( + private val app: TelegramApplication, + private val userId: Int, + private val chatId: Long, + private val deviceName: String, + private val start: Long, + private val end: Long, + private val listener: LoadGpxListener + ) : + AsyncTask() { + + override fun doInBackground(vararg params: Void): GpxDataItem? { + val locationMessages = app.locationMessages.getMessagesForUserInChat(userId, chatId, deviceName, start, end) + if (locationMessages.isNotEmpty() && !isCancelled) { + val items = OsmandLocationUtils.convertLocationMessagesToGpxFiles(app, locationMessages) + if (items.isNotEmpty() && !isCancelled) { + val gpx = items.firstOrNull() + if (gpx != null) { + val analysis = gpx.getAnalysis(0) + return if (!isCancelled) GpxDataItem(gpx, analysis) else null + } + } } - DatePickerDialog(context, dateFromDialog, - startCalendar.get(Calendar.YEAR), - startCalendar.get(Calendar.MONTH), - startCalendar.get(Calendar.DAY_OF_MONTH)).show() - } + return null + } - private fun selectStartTime() { - TimePickerDialog(context, - TimePickerDialog.OnTimeSetListener { _, hours, minutes -> - startCalendar.set(Calendar.HOUR_OF_DAY, hours) - startCalendar.set(Calendar.MINUTE, minutes) - updateGpxInfo() - }, 0, 0, true).show() - } - - private fun selectEndDate() { - val dateFromDialog = - DatePickerDialog.OnDateSetListener { _, year, monthOfYear, dayOfMonth -> - endCalendar.set(Calendar.YEAR, year) - endCalendar.set(Calendar.MONTH, monthOfYear) - endCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) - endTimeChanged = true - updateGpxInfo() + override fun onPostExecute(gpxDataItem: GpxDataItem?) { + if (gpxDataItem != null) { + listener.onLoadGpxFinish(gpxDataItem) + } else { + listener.onLoadGpxError("Cant create gpx for $userId $chatId $deviceName $start $end") } - DatePickerDialog(context, dateFromDialog, - endCalendar.get(Calendar.YEAR), - endCalendar.get(Calendar.MONTH), - endCalendar.get(Calendar.DAY_OF_MONTH)).show() + } } - private fun selectEndTime() { - TimePickerDialog(context, - TimePickerDialog.OnTimeSetListener { _, hours, minutes -> - endCalendar.set(Calendar.HOUR_OF_DAY, hours) - endCalendar.set(Calendar.MINUTE, minutes) - endTimeChanged = true - updateGpxInfo() - }, 0, 0, true).show() + interface LoadGpxListener { + + fun onLoadGpxFinish(dataItem: GpxDataItem) + + fun onLoadGpxError(error: String) + } + data class GpxDataItem( + val gpxFile: GPXUtilities.GPXFile, + val analysis: GPXUtilities.GPXTrackAnalysis + ) + companion object { private const val TAG = "UserGpxInfoFragment" @@ -460,13 +479,14 @@ class UserGpxInfoFragment : BaseDialogFragment() { private const val USER_ID_KEY = "user_id_key" private const val CHAT_ID_KEY = "chat_id_key" private const val DEVICE_NAME_KEY = "device_name_key" + private const val DATE_FORMAT = "dd MMM HH:mm" private const val GPX_TRACK_COLOR = -65536 private const val MIN_OSMAND_BITMAP_VERSION_CODE = 330 private const val UPDATE_TRACK_INTERVAL_MS = 30 * 1000L // 30 sec private const val TRACK_UPDATE_MSG_ID = 1001 - fun showInstance(fm: FragmentManager,userId:Int,chatId:Long,deviceName:String, start: Long, end: Long): Boolean { + fun showInstance(fm: FragmentManager, userId: Int, chatId: Long, deviceName: String, start: Long, end: Long): Boolean { return try { val fragment = UserGpxInfoFragment().apply { arguments = Bundle().apply { diff --git a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandFormatter.kt b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandFormatter.kt index 3a9719848a..3ca79e9867 100644 --- a/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandFormatter.kt +++ b/OsmAnd-telegram/src/net/osmand/telegram/utils/OsmandFormatter.kt @@ -63,14 +63,14 @@ object OsmandFormatter { } } - fun getFormattedTime(milliseconds: Long, useCurrentTime: Boolean = true): String { + fun getFormattedTime(milliseconds: Long, useCurrentTime: Boolean = true, short: Boolean = false): String { val calendar = Calendar.getInstance() if (useCurrentTime) { calendar.timeInMillis = System.currentTimeMillis() + milliseconds } else { calendar.timeInMillis = milliseconds } - return if (isSameDay(calendar, Calendar.getInstance())) { + return if (isSameDay(calendar, Calendar.getInstance()) || short) { SimpleDateFormat(SIMPLE_TIME_OF_DAY_FORMAT, Locale.getDefault()).format(calendar.time) } else { SimpleDateFormat(SIMPLE_TIME_OF_DAY_FORMAT, Locale.getDefault()).format(calendar.time) +