Merge pull request #8185 from osmandapp/Widget_for_Tracker_in_OsmAnd
Widget for tracker in osm and
This commit is contained in:
commit
a6300d5c3d
10 changed files with 157 additions and 0 deletions
|
@ -1,5 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="status_widget_title">OsmAnd Tracker status</string>
|
||||||
<string name="buffer_time_descr">Maximum time to store points in the buffer</string>
|
<string name="buffer_time_descr">Maximum time to store points in the buffer</string>
|
||||||
<string name="buffer_time">Buffer expiration time</string>
|
<string name="buffer_time">Buffer expiration time</string>
|
||||||
<string name="time_zone_descr">Select time zone to show in your location messages.</string>
|
<string name="time_zone_descr">Select time zone to show in your location messages.</string>
|
||||||
|
|
|
@ -56,6 +56,7 @@ class TelegramApplication : Application(), OsmandHelperListener {
|
||||||
)
|
)
|
||||||
showLocationHelper.addDirectionContextMenuButton()
|
showLocationHelper.addDirectionContextMenuButton()
|
||||||
showLocationHelper.startShowingLocation()
|
showLocationHelper.startShowingLocation()
|
||||||
|
showLocationHelper.addOrUpdateStatusWidget(-1, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,15 @@ import net.osmand.telegram.utils.AndroidUtils
|
||||||
import org.drinkless.td.libcore.telegram.TdApi
|
import org.drinkless.td.libcore.telegram.TdApi
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
private const val UPDATE_WIDGET_INTERVAL_MS = 1000L // 1 sec
|
||||||
private const val UPDATE_LIVE_MESSAGES_INTERVAL_MS = 10000L // 10 sec
|
private const val UPDATE_LIVE_MESSAGES_INTERVAL_MS = 10000L // 10 sec
|
||||||
private const val UPDATE_LIVE_TRACKS_INTERVAL_MS = 30000L // 30 sec
|
private const val UPDATE_LIVE_TRACKS_INTERVAL_MS = 30000L // 30 sec
|
||||||
|
|
||||||
class TelegramService : Service(), LocationListener, TelegramIncomingMessagesListener,
|
class TelegramService : Service(), LocationListener, TelegramIncomingMessagesListener,
|
||||||
TelegramOutgoingMessagesListener {
|
TelegramOutgoingMessagesListener {
|
||||||
|
|
||||||
|
private val log = PlatformUtil.getLog(TelegramService::class.java)
|
||||||
|
|
||||||
private fun app() = application as TelegramApplication
|
private fun app() = application as TelegramApplication
|
||||||
private val binder = LocationServiceBinder()
|
private val binder = LocationServiceBinder()
|
||||||
private var shouldCleanupResources: Boolean = false
|
private var shouldCleanupResources: Boolean = false
|
||||||
|
@ -36,6 +39,9 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
|
||||||
private var updateTracksHandler: Handler? = null
|
private var updateTracksHandler: Handler? = null
|
||||||
private var tracksHandlerThread = HandlerThread("TracksUpdateServiceThread")
|
private var tracksHandlerThread = HandlerThread("TracksUpdateServiceThread")
|
||||||
|
|
||||||
|
private var updateWidgetHandler: Handler? = null
|
||||||
|
private var updateWidgetThread = HandlerThread("WidgetUpdateServiceThread")
|
||||||
|
|
||||||
var handler: Handler? = null
|
var handler: Handler? = null
|
||||||
private set
|
private set
|
||||||
var usedBy = 0
|
var usedBy = 0
|
||||||
|
@ -58,8 +64,10 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
mHandlerThread.start()
|
mHandlerThread.start()
|
||||||
tracksHandlerThread.start()
|
tracksHandlerThread.start()
|
||||||
|
updateWidgetThread.start()
|
||||||
updateShareInfoHandler = Handler(mHandlerThread.looper)
|
updateShareInfoHandler = Handler(mHandlerThread.looper)
|
||||||
updateTracksHandler = Handler(tracksHandlerThread.looper)
|
updateTracksHandler = Handler(tracksHandlerThread.looper)
|
||||||
|
updateWidgetHandler = Handler(updateWidgetThread.looper)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
override fun onBind(intent: Intent): IBinder? {
|
||||||
|
@ -106,6 +114,7 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
|
||||||
if (isUsedByMyLocation(usedBy)) {
|
if (isUsedByMyLocation(usedBy)) {
|
||||||
initLocationUpdates()
|
initLocationUpdates()
|
||||||
startShareInfoUpdates()
|
startShareInfoUpdates()
|
||||||
|
startWidgetUpdates()
|
||||||
}
|
}
|
||||||
if (isUsedByUsersLocations(usedBy)) {
|
if (isUsedByUsersLocations(usedBy)) {
|
||||||
app.telegramHelper.startLiveMessagesUpdates(app.settings.sendMyLocInterval)
|
app.telegramHelper.startLiveMessagesUpdates(app.settings.sendMyLocInterval)
|
||||||
|
@ -139,6 +148,8 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
|
||||||
app.telegramService = null
|
app.telegramService = null
|
||||||
tracksHandlerThread.quit()
|
tracksHandlerThread.quit()
|
||||||
mHandlerThread.quit()
|
mHandlerThread.quit()
|
||||||
|
updateWidgetThread.quit()
|
||||||
|
app().showLocationHelper.addOrUpdateStatusWidget(-1, false)
|
||||||
|
|
||||||
usedBy = 0
|
usedBy = 0
|
||||||
|
|
||||||
|
@ -209,6 +220,33 @@ class TelegramService : Service(), LocationListener, TelegramIncomingMessagesLis
|
||||||
}
|
}
|
||||||
}, UPDATE_LIVE_TRACKS_INTERVAL_MS)
|
}, UPDATE_LIVE_TRACKS_INTERVAL_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startWidgetUpdates() {
|
||||||
|
updateWidgetHandler?.postDelayed({
|
||||||
|
if (isUsedByMyLocation(usedBy)) {
|
||||||
|
val sharingStatus = app().settings.sharingStatusChanges.last()
|
||||||
|
val isSending = sharingStatus.statusType == TelegramSettings.SharingStatusType.SENDING
|
||||||
|
val sharingChats = app().settings.getShareLocationChats()
|
||||||
|
var oldestTime = 0L
|
||||||
|
if (sharingChats.isNotEmpty()) {
|
||||||
|
sharingChats.forEach { id ->
|
||||||
|
val bufferMessages = app().locationMessages.getBufferedMessagesForChat(id)
|
||||||
|
if (bufferMessages.isNotEmpty()) {
|
||||||
|
val newTime = bufferMessages[0].time
|
||||||
|
if (oldestTime == 0L || newTime < oldestTime) {
|
||||||
|
oldestTime = newTime
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oldestTime = 0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.info("oldest buffered msg time: $oldestTime | isSending: $isSending")
|
||||||
|
app().showLocationHelper.addOrUpdateStatusWidget(oldestTime, isSending)
|
||||||
|
}
|
||||||
|
startWidgetUpdates()
|
||||||
|
}, UPDATE_WIDGET_INTERVAL_MS)
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
private fun getFirstTimeRunDefaultLocation(): net.osmand.Location? {
|
private fun getFirstTimeRunDefaultLocation(): net.osmand.Location? {
|
||||||
|
|
|
@ -43,6 +43,15 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
||||||
|
|
||||||
const val GPX_COLORS_COUNT = 10
|
const val GPX_COLORS_COUNT = 10
|
||||||
|
|
||||||
|
private const val STATUS_WIDGET_ID = "status_widget"
|
||||||
|
private const val STATUS_WIDGET_MENU_ICON = "widget_location_sharing_night"
|
||||||
|
private const val STATUS_WIDGET_ANIM_ICON_DAY = "anim_widget_location_sharing_day"
|
||||||
|
private const val STATUS_WIDGET_ANIM_ICON_NIGHT = "anim_widget_location_sharing_night"
|
||||||
|
private const val STATUS_WIDGET_ON_ANIM_ICON_DAY = "anim_widget_location_sharing_on_day"
|
||||||
|
private const val STATUS_WIDGET_ON_ANIM_ICON_NIGHT = "anim_widget_location_sharing_on_night"
|
||||||
|
private const val STATUS_WIDGET_OFF_ICON_DAY = "widget_location_sharing_off_day"
|
||||||
|
private const val STATUS_WIDGET_OFF_ICON_NIGHT = "widget_location_sharing_off_night"
|
||||||
|
|
||||||
val GPX_COLORS = arrayOf(
|
val GPX_COLORS = arrayOf(
|
||||||
"red", "orange", "lightblue", "blue", "purple",
|
"red", "orange", "lightblue", "blue", "purple",
|
||||||
"translucent_red", "translucent_orange", "translucent_lightblue",
|
"translucent_red", "translucent_orange", "translucent_lightblue",
|
||||||
|
@ -198,6 +207,57 @@ class ShowLocationHelper(private val app: TelegramApplication) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addOrUpdateStatusWidget(time: Long, isSending: Boolean) {
|
||||||
|
val iconDay: String
|
||||||
|
val iconNight: String
|
||||||
|
val text = when {
|
||||||
|
time > 0L -> {
|
||||||
|
iconDay = STATUS_WIDGET_ANIM_ICON_DAY
|
||||||
|
iconNight = STATUS_WIDGET_ANIM_ICON_NIGHT
|
||||||
|
val diffTime = (System.currentTimeMillis() - time) / 1000
|
||||||
|
OsmandFormatter.getFormattedDurationForWidget(diffTime)
|
||||||
|
}
|
||||||
|
time == 0L && isSending -> {
|
||||||
|
iconDay = STATUS_WIDGET_ON_ANIM_ICON_DAY
|
||||||
|
iconNight = STATUS_WIDGET_ON_ANIM_ICON_NIGHT
|
||||||
|
app.getString(R.string.shared_string_ok)
|
||||||
|
}
|
||||||
|
time == 0L && !isSending -> {
|
||||||
|
iconDay = STATUS_WIDGET_ANIM_ICON_DAY
|
||||||
|
iconNight = STATUS_WIDGET_ANIM_ICON_NIGHT
|
||||||
|
app.getString(R.string.shared_string_ok)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
iconDay = STATUS_WIDGET_OFF_ICON_DAY
|
||||||
|
iconNight = STATUS_WIDGET_OFF_ICON_NIGHT
|
||||||
|
app.getString(R.string.shared_string_start)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val subText = when {
|
||||||
|
time > 0 -> {
|
||||||
|
if (text.length > 2) {
|
||||||
|
app.getString(R.string.shared_string_hour_short)
|
||||||
|
} else {
|
||||||
|
app.getString(R.string.shared_string_minute_short)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> ""
|
||||||
|
}
|
||||||
|
osmandAidlHelper.addMapWidget(
|
||||||
|
STATUS_WIDGET_ID,
|
||||||
|
STATUS_WIDGET_MENU_ICON,
|
||||||
|
app.getString(R.string.status_widget_title),
|
||||||
|
iconDay,
|
||||||
|
iconNight,
|
||||||
|
text, subText, 50, getStatusWidgetIntent())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStatusWidgetIntent(): Intent {
|
||||||
|
val startIntent = app.packageManager.getLaunchIntentForPackage(app.packageName)
|
||||||
|
startIntent.addCategory(Intent.CATEGORY_LAUNCHER)
|
||||||
|
return startIntent
|
||||||
|
}
|
||||||
|
|
||||||
private fun getALatLonFromMessage(content: TdApi.MessageContent): ALatLon? {
|
private fun getALatLonFromMessage(content: TdApi.MessageContent): ALatLon? {
|
||||||
return when (content) {
|
return when (content) {
|
||||||
is TdApi.MessageLocation -> ALatLon(content.location.latitude, content.location.longitude)
|
is TdApi.MessageLocation -> ALatLon(content.location.latitude, content.location.longitude)
|
||||||
|
|
|
@ -45,6 +45,18 @@ object OsmandFormatter {
|
||||||
fixed2.minimumIntegerDigits = 1
|
fixed2.minimumIntegerDigits = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getFormattedDurationForWidget(seconds: Long): String {
|
||||||
|
val hours = seconds / (60 * 60)
|
||||||
|
val minutes = seconds / 60 % 60
|
||||||
|
return when {
|
||||||
|
hours > 9 -> String.format("%10d:%01d", hours, minutes)
|
||||||
|
hours > 0 -> String.format("%1d:%01d", hours, minutes)
|
||||||
|
minutes > 9 -> String.format("%11d", minutes)
|
||||||
|
minutes > 0 -> String.format("%1d", minutes)
|
||||||
|
else -> "1"
|
||||||
|
}.trim()
|
||||||
|
}
|
||||||
|
|
||||||
fun getFormattedDuration(ctx: Context, seconds: Long, short: Boolean = false): String {
|
fun getFormattedDuration(ctx: Context, seconds: Long, short: Boolean = false): String {
|
||||||
val hours = seconds / (60 * 60)
|
val hours = seconds / (60 * 60)
|
||||||
val minutes = seconds / 60 % 60
|
val minutes = seconds / 60 % 60
|
||||||
|
|
10
OsmAnd/res/drawable/anim_widget_location_sharing_day.xml
Normal file
10
OsmAnd/res/drawable/anim_widget_location_sharing_day.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:oneshot="false">
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/widget_location_sharing_day"
|
||||||
|
android:duration="500" />
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/widget_location_sharing_off_day"
|
||||||
|
android:duration="500" />
|
||||||
|
</animation-list>
|
10
OsmAnd/res/drawable/anim_widget_location_sharing_night.xml
Normal file
10
OsmAnd/res/drawable/anim_widget_location_sharing_night.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:oneshot="false">
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/widget_location_sharing_night"
|
||||||
|
android:duration="500" />
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/widget_location_sharing_off_night"
|
||||||
|
android:duration="500" />
|
||||||
|
</animation-list>
|
10
OsmAnd/res/drawable/anim_widget_location_sharing_on_day.xml
Normal file
10
OsmAnd/res/drawable/anim_widget_location_sharing_on_day.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:oneshot="false">
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/widget_location_sharing_on_day"
|
||||||
|
android:duration="500" />
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/widget_location_sharing_off_day"
|
||||||
|
android:duration="500" />
|
||||||
|
</animation-list>
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:oneshot="false">
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/widget_location_sharing_on_night"
|
||||||
|
android:duration="500" />
|
||||||
|
<item
|
||||||
|
android:drawable="@drawable/widget_location_sharing_off_night"
|
||||||
|
android:duration="500" />
|
||||||
|
</animation-list>
|
|
@ -3,6 +3,7 @@ package net.osmand.plus.views.mapwidgets;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.graphics.Paint.Style;
|
import android.graphics.Paint.Style;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.AnimationDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
|
@ -71,6 +72,10 @@ public class TextInfoWidget {
|
||||||
public void setImageDrawable(Drawable imageDrawable, boolean gone) {
|
public void setImageDrawable(Drawable imageDrawable, boolean gone) {
|
||||||
if(imageDrawable != null) {
|
if(imageDrawable != null) {
|
||||||
imageView.setImageDrawable(imageDrawable);
|
imageView.setImageDrawable(imageDrawable);
|
||||||
|
Object anim = imageView.getDrawable();
|
||||||
|
if (anim instanceof AnimationDrawable) {
|
||||||
|
((AnimationDrawable) anim).start();
|
||||||
|
}
|
||||||
imageView.setVisibility(View.VISIBLE);
|
imageView.setVisibility(View.VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
imageView.setVisibility(gone ? View.GONE : View.INVISIBLE);
|
imageView.setVisibility(gone ? View.GONE : View.INVISIBLE);
|
||||||
|
|
Loading…
Reference in a new issue