Fix api
This commit is contained in:
parent
163ef6197b
commit
5802c52b97
|
@ -1,10 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RunConfigurationProducerService">
|
|
||||||
<option name="ignoredProducers">
|
|
||||||
<set>
|
|
||||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -4,13 +4,13 @@ plugins {
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 31
|
||||||
buildToolsVersion "30.0.3"
|
buildToolsVersion "30.0.3"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "fr.oupson.taotoolbox"
|
applicationId "fr.oupson.taotoolbox"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
targetSdkVersion 31
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "0.0.3"
|
versionName "0.0.3"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
@ -43,19 +43,17 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation 'androidx.core:core-ktx:1.7.0'
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation 'androidx.appcompat:appcompat:1.4.0'
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'com.google.android.material:material:1.4.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
implementation 'androidx.preference:preference-ktx:1.1.1'
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
|
||||||
|
|
||||||
implementation project(":common")
|
implementation project(":common")
|
||||||
implementation 'org.osmdroid:osmdroid-android:6.1.10'
|
implementation 'org.osmdroid:osmdroid-android:6.1.10'
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="fr.oupson.taotoolbox">
|
package="fr.oupson.taotoolbox">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
@ -15,13 +16,17 @@
|
||||||
android:roundIcon="@mipmap/ic_launcher"
|
android:roundIcon="@mipmap/ic_launcher"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/Theme.TaoToolbox">
|
android:theme="@style/Theme.TaoToolbox">
|
||||||
<activity android:name=".activities.TaoWidgetConfigurationActivity">
|
<activity
|
||||||
|
android:name=".activities.TaoWidgetConfigurationActivity"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<receiver android:name=".receivers.TaoWidget">
|
<receiver
|
||||||
|
android:name=".receivers.TaoWidget"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
@ -31,7 +36,9 @@
|
||||||
android:resource="@xml/widget_tao_info" />
|
android:resource="@xml/widget_tao_info" />
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
<activity android:name=".activities.MainActivity">
|
<activity
|
||||||
|
android:name=".activities.MainActivity"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ import android.view.WindowManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import fr.oupson.common.api.TaoRestApi
|
import fr.oupson.common.api.TaoRestApi
|
||||||
import fr.oupson.common.db.TaoDatabaseHelper
|
import fr.oupson.common.db.TaoDatabaseHelper
|
||||||
|
@ -20,6 +21,7 @@ import fr.oupson.common.db.TaoDatabaseHelper.TaoDatabase.StopEntry
|
||||||
import fr.oupson.taotoolbox.BuildConfig
|
import fr.oupson.taotoolbox.BuildConfig
|
||||||
import fr.oupson.taotoolbox.R
|
import fr.oupson.taotoolbox.R
|
||||||
import fr.oupson.taotoolbox.databinding.ActivityMainBinding
|
import fr.oupson.taotoolbox.databinding.ActivityMainBinding
|
||||||
|
import fr.oupson.taotoolbox.utils.PolylineDecoder
|
||||||
import fr.oupson.taotoolbox.windows.StopInfoWindow
|
import fr.oupson.taotoolbox.windows.StopInfoWindow
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
|
@ -98,7 +100,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
requestPermissionsIfNecessary()
|
requestPermissionsIfNecessary()
|
||||||
|
|
||||||
GlobalScope.launch(scope) {
|
lifecycleScope.launch(scope) {
|
||||||
val helper = TaoDatabaseHelper(this@MainActivity).apply {
|
val helper = TaoDatabaseHelper(this@MainActivity).apply {
|
||||||
try {
|
try {
|
||||||
this.checkUpdate()
|
this.checkUpdate()
|
||||||
|
@ -241,12 +243,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val geo = route.getJSONArray("geojson") // TODO DECODE ENCODED ?
|
PolylineDecoder.decodeInto(routeLine, route.getString("geojsonEncoded"), 1)
|
||||||
|
|
||||||
for (i in 0 until geo.length()) {
|
|
||||||
val value = geo.getJSONArray(i)
|
|
||||||
routeLine.addPoint(GeoPoint(value.getDouble(1), value.getDouble(0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.map.overlays.add(routeLine)
|
binding.map.overlays.add(routeLine)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.AutoCompleteTextView
|
import android.widget.AutoCompleteTextView
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import fr.oupson.common.api.TaoRestApi
|
import fr.oupson.common.api.TaoRestApi
|
||||||
import fr.oupson.common.db.TaoDatabaseHelper
|
import fr.oupson.common.db.TaoDatabaseHelper
|
||||||
import fr.oupson.common.db.TaoDatabaseHelper.TaoDatabase.*
|
import fr.oupson.common.db.TaoDatabaseHelper.TaoDatabase.*
|
||||||
|
@ -69,7 +70,7 @@ class TaoWidgetConfigurationActivity : AppCompatActivity() {
|
||||||
val appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(this)
|
val appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(this)
|
||||||
val ids =
|
val ids =
|
||||||
appWidgetManager.getAppWidgetIds(ComponentName(this, TaoWidget::class.java))
|
appWidgetManager.getAppWidgetIds(ComponentName(this, TaoWidget::class.java))
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
for (id in ids) {
|
for (id in ids) {
|
||||||
TaoWidget.updateAppWidget(
|
TaoWidget.updateAppWidget(
|
||||||
this@TaoWidgetConfigurationActivity,
|
this@TaoWidgetConfigurationActivity,
|
||||||
|
@ -88,7 +89,7 @@ class TaoWidgetConfigurationActivity : AppCompatActivity() {
|
||||||
(binding.configSelectLine.editText as? AutoCompleteTextView)?.also { autoCompleteTextView ->
|
(binding.configSelectLine.editText as? AutoCompleteTextView)?.also { autoCompleteTextView ->
|
||||||
autoCompleteTextView.setAdapter(lineAdapter)
|
autoCompleteTextView.setAdapter(lineAdapter)
|
||||||
autoCompleteTextView.setOnItemClickListener { _, _, position, _ ->
|
autoCompleteTextView.setOnItemClickListener { _, _, position, _ ->
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
linePosition = position
|
linePosition = position
|
||||||
directionPosition = -1
|
directionPosition = -1
|
||||||
loadDirectionList(position)
|
loadDirectionList(position)
|
||||||
|
@ -99,7 +100,7 @@ class TaoWidgetConfigurationActivity : AppCompatActivity() {
|
||||||
(binding.configSelectDirection.editText as? AutoCompleteTextView)?.also { directionAutoComplete ->
|
(binding.configSelectDirection.editText as? AutoCompleteTextView)?.also { directionAutoComplete ->
|
||||||
directionAutoComplete.setAdapter(directionAdapter)
|
directionAutoComplete.setAdapter(directionAdapter)
|
||||||
directionAutoComplete.setOnItemClickListener { _, _, position, _ ->
|
directionAutoComplete.setOnItemClickListener { _, _, position, _ ->
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
directionPosition = position
|
directionPosition = position
|
||||||
stopPosition = -1
|
stopPosition = -1
|
||||||
|
|
||||||
|
@ -120,7 +121,7 @@ class TaoWidgetConfigurationActivity : AppCompatActivity() {
|
||||||
save()
|
save()
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
db = TaoDatabaseHelper(this@TaoWidgetConfigurationActivity).apply {
|
db = TaoDatabaseHelper(this@TaoWidgetConfigurationActivity).apply {
|
||||||
checkUpdate()
|
checkUpdate()
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
|
@ -7,18 +7,19 @@ import android.view.ViewGroup
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import fr.oupson.taotoolbox.R
|
|
||||||
import fr.oupson.common.api.LineColors
|
import fr.oupson.common.api.LineColors
|
||||||
import fr.oupson.common.api.Schedule
|
import fr.oupson.common.api.RealtimeStopArea
|
||||||
|
import fr.oupson.taotoolbox.R
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class ScheduleAdapter(
|
// TODO ADAPT
|
||||||
private val scheduleList: SimplePtr<Array<Schedule>>
|
class RealTimeAdapter(
|
||||||
|
private val realTimeList: SimplePtr<Array<RealtimeStopArea>>
|
||||||
) :
|
) :
|
||||||
RecyclerView.Adapter<ScheduleAdapter.ScheduleViewHolder>() {
|
RecyclerView.Adapter<RealTimeAdapter.RealTimeViewHolder>() {
|
||||||
class SimplePtr<T>(private var value: T? = null) {
|
class SimplePtr<T>(private var value: T? = null) {
|
||||||
fun get(): T? = value
|
fun get(): T? = value
|
||||||
fun set(value: T?) {
|
fun set(value: T?) {
|
||||||
|
@ -26,21 +27,23 @@ class ScheduleAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScheduleViewHolder(
|
class RealTimeViewHolder(
|
||||||
private val view: View
|
private val view: View
|
||||||
) : RecyclerView.ViewHolder(view) {
|
) : RecyclerView.ViewHolder(view) {
|
||||||
private val scheduleDirectionTextView =
|
private val scheduleDirectionTextView =
|
||||||
view.findViewById<TextView>(R.id.item_schedule_direction_text_view)
|
view.findViewById<TextView>(R.id.item_schedule_direction_text_view)
|
||||||
private val scheduleImageView = view.findViewById<ImageView>(R.id.item_schedule_line_image_view)
|
private val scheduleImageView =
|
||||||
private val scheduleNextTextView = view.findViewById<TextView>(R.id.item_schedule_next_text_view)
|
view.findViewById<ImageView>(R.id.item_schedule_line_image_view)
|
||||||
|
private val scheduleNextTextView =
|
||||||
|
view.findViewById<TextView>(R.id.item_schedule_next_text_view)
|
||||||
private val scheduleAfterTextView =
|
private val scheduleAfterTextView =
|
||||||
view.findViewById<TextView>(R.id.item_schedule_after_text_view)
|
view.findViewById<TextView>(R.id.item_schedule_after_text_view)
|
||||||
|
|
||||||
fun bind(schedule: Schedule) {
|
fun bind(schedule: RealtimeStopArea) {
|
||||||
scheduleDirectionTextView.text = view.context.getString(
|
scheduleDirectionTextView.text = view.context.getString(
|
||||||
R.string.line_and_direction_name,
|
R.string.line_and_direction_name,
|
||||||
schedule.lineColors.lineCode,
|
schedule.lineColors.lineCode,
|
||||||
schedule.lineDirectionName
|
schedule.routeName
|
||||||
)
|
)
|
||||||
|
|
||||||
scheduleNextTextView.text =
|
scheduleNextTextView.text =
|
||||||
|
@ -50,7 +53,6 @@ class ScheduleAdapter(
|
||||||
schedule.timeRemainingAfter() ?: "∅"
|
schedule.timeRemainingAfter() ?: "∅"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.Default) {
|
GlobalScope.launch(Dispatchers.Default) {
|
||||||
val btm = LineColors.getLineBtm(
|
val btm = LineColors.getLineBtm(
|
||||||
view.context,
|
view.context,
|
||||||
|
@ -67,16 +69,16 @@ class ScheduleAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ScheduleViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RealTimeViewHolder {
|
||||||
val inflater = LayoutInflater.from(parent.context)
|
val inflater = LayoutInflater.from(parent.context)
|
||||||
return ScheduleViewHolder(inflater.inflate(R.layout.item_schedule, parent, false))
|
return RealTimeViewHolder(inflater.inflate(R.layout.item_schedule, parent, false))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ScheduleViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: RealTimeViewHolder, position: Int) {
|
||||||
val ref = scheduleList.get()
|
val ref = realTimeList.get()
|
||||||
if (ref != null)
|
if (ref != null)
|
||||||
holder.bind(ref[position])
|
holder.bind(ref[position])
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = scheduleList.get()?.size ?: 0
|
override fun getItemCount(): Int = realTimeList.get()?.size ?: 0
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.*
|
import android.graphics.*
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import fr.oupson.common.api.LineColors
|
import fr.oupson.common.api.LineColors
|
||||||
|
@ -37,12 +38,18 @@ class TaoWidget : AppWidgetProvider() {
|
||||||
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(appWidgetId))
|
serviceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(appWidgetId))
|
||||||
serviceIntent.data = Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME))
|
serviceIntent.data = Uri.parse(serviceIntent.toUri(Intent.URI_INTENT_SCHEME))
|
||||||
|
|
||||||
val pending = PendingIntent.getBroadcast(
|
val pending =
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
context,
|
context,
|
||||||
appWidgetId,
|
appWidgetId,
|
||||||
serviceIntent,
|
serviceIntent,
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
|
||||||
|
} else {
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
views.setOnClickPendingIntent(R.id.tao_widget_root, pending)
|
views.setOnClickPendingIntent(R.id.tao_widget_root, pending)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
package fr.oupson.taotoolbox.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import org.osmdroid.util.GeoPoint;
|
||||||
|
import org.osmdroid.views.overlay.Polyline;
|
||||||
|
|
||||||
|
public class PolylineDecoder {
|
||||||
|
public static void decodeInto(Polyline line, String encodedString, int precision) {
|
||||||
|
int index = 0;
|
||||||
|
int len = encodedString.length();
|
||||||
|
int lat = 0, lng = 0;
|
||||||
|
|
||||||
|
while (index < len) {
|
||||||
|
int b, shift, result;
|
||||||
|
shift = result = 0;
|
||||||
|
do {
|
||||||
|
b = encodedString.charAt(index++) - 63;
|
||||||
|
result |= (b & 0x1f) << shift;
|
||||||
|
shift += 5;
|
||||||
|
} while (b >= 0x20);
|
||||||
|
int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
|
||||||
|
lat += dlat;
|
||||||
|
|
||||||
|
shift = result = 0;
|
||||||
|
do {
|
||||||
|
b = encodedString.charAt(index++) - 63;
|
||||||
|
result |= (b & 0x1f) << shift;
|
||||||
|
shift += 5;
|
||||||
|
} while (b >= 0x20);
|
||||||
|
int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
|
||||||
|
lng += dlng;
|
||||||
|
|
||||||
|
GeoPoint p = new GeoPoint(((double) (lng * precision)) / 1.0e6, ((double) (lat * precision)) / 1.0e6, 0);
|
||||||
|
line.addPoint(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,10 +3,10 @@ package fr.oupson.taotoolbox.windows
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import fr.oupson.taotoolbox.R
|
import fr.oupson.common.api.RealtimeStopArea
|
||||||
import fr.oupson.taotoolbox.adapters.ScheduleAdapter
|
|
||||||
import fr.oupson.common.api.Schedule
|
|
||||||
import fr.oupson.common.api.TaoRestApi
|
import fr.oupson.common.api.TaoRestApi
|
||||||
|
import fr.oupson.taotoolbox.R
|
||||||
|
import fr.oupson.taotoolbox.adapters.RealTimeAdapter
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import org.osmdroid.views.MapView
|
import org.osmdroid.views.MapView
|
||||||
import org.osmdroid.views.overlay.Marker
|
import org.osmdroid.views.overlay.Marker
|
||||||
|
@ -21,13 +21,13 @@ class StopInfoWindow(
|
||||||
|
|
||||||
private val titleTextView: TextView by lazy { view.findViewById(R.id.window_stop_info_title_text_view) }
|
private val titleTextView: TextView by lazy { view.findViewById(R.id.window_stop_info_title_text_view) }
|
||||||
|
|
||||||
private val scheduleList: ScheduleAdapter.SimplePtr<Array<Schedule>> by lazy {
|
private val realtimeList: RealTimeAdapter.SimplePtr<Array<RealtimeStopArea>> by lazy {
|
||||||
ScheduleAdapter.SimplePtr(
|
RealTimeAdapter.SimplePtr(
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val adapter: ScheduleAdapter by lazy { ScheduleAdapter(scheduleList) }
|
private val adapter: RealTimeAdapter by lazy { RealTimeAdapter(realtimeList) }
|
||||||
private val recyclerView: RecyclerView by lazy {
|
private val recyclerView: RecyclerView by lazy {
|
||||||
view.findViewById<RecyclerView>(R.id.window_stop_info_schedule_recycler_view).also {
|
view.findViewById<RecyclerView>(R.id.window_stop_info_schedule_recycler_view).also {
|
||||||
it.adapter = adapter
|
it.adapter = adapter
|
||||||
|
@ -36,7 +36,7 @@ class StopInfoWindow(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOpen(item: Any?) {
|
override fun onOpen(item: Any?) {
|
||||||
scheduleList.set(null)
|
realtimeList.set(null)
|
||||||
recyclerView.adapter?.notifyDataSetChanged()
|
recyclerView.adapter?.notifyDataSetChanged()
|
||||||
|
|
||||||
if (item is Marker) {
|
if (item is Marker) {
|
||||||
|
@ -44,10 +44,10 @@ class StopInfoWindow(
|
||||||
titleTextView.text = item.title
|
titleTextView.text = item.title
|
||||||
|
|
||||||
GlobalScope.launch(windowsContext) {
|
GlobalScope.launch(windowsContext) {
|
||||||
val schedule = taoRestApi.getSchedule(id)
|
val realtime = taoRestApi.getRealtimeByStopArea(id)
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
scheduleList.set(schedule)
|
realtimeList.set(realtime)
|
||||||
adapter.notifyDataSetChanged()
|
adapter.notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = "1.5.0"
|
ext.kotlin_version = "1.6.10"
|
||||||
ext.ktor_version = "1.5.3"
|
ext.ktor_version = "1.6.3"
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.2.0'
|
classpath 'com.android.tools.build:gradle:7.0.4'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
|
|
@ -1,18 +1,16 @@
|
||||||
plugins {
|
plugins {
|
||||||
id 'com.android.library'
|
id 'com.android.library'
|
||||||
id 'kotlin-android'
|
id 'kotlin-android'
|
||||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.4.30'
|
id 'org.jetbrains.kotlin.plugin.serialization' version '1.5.30'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 31
|
||||||
buildToolsVersion "30.0.3"
|
buildToolsVersion "30.0.3"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 30
|
targetSdkVersion 31
|
||||||
versionCode 1
|
|
||||||
versionName "1.0"
|
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
consumerProguardFiles "consumer-rules.pro"
|
consumerProguardFiles "consumer-rules.pro"
|
||||||
|
@ -34,14 +32,13 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation 'androidx.core:core-ktx:1.7.0'
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.appcompat:appcompat:1.4.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'com.google.android.material:material:1.4.0'
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.1.0"
|
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
|
||||||
|
|
||||||
implementation "io.ktor:ktor-client-serialization:$ktor_version"
|
implementation "io.ktor:ktor-client-serialization:$ktor_version"
|
||||||
implementation "io.ktor:ktor-client-core:$ktor_version"
|
implementation "io.ktor:ktor-client-core:$ktor_version"
|
||||||
|
|
|
@ -130,7 +130,10 @@ object DateAsStringSerializer : KSerializer<Date> {
|
||||||
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.getDefault()).parse(string)!!
|
SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSX", Locale.getDefault()).parse(string)!!
|
||||||
} else { // Z format is not supported :/
|
} else { // Z format is not supported :/
|
||||||
val newDateStr = if (string.endsWith("Z")) {
|
val newDateStr = if (string.endsWith("Z")) {
|
||||||
string.substring(0, string.length - 1) + "+0000" // Little trick to get time in correct time zone
|
string.substring(
|
||||||
|
0,
|
||||||
|
string.length - 1
|
||||||
|
) + "+0000" // Little trick to get time in correct time zone
|
||||||
} else {
|
} else {
|
||||||
throw Exception("$string is not supported") // TODO ?
|
throw Exception("$string is not supported") // TODO ?
|
||||||
}
|
}
|
||||||
|
@ -165,3 +168,64 @@ data class Schedule(
|
||||||
(it.time - Date().time) / (1000 * 60)
|
(it.time - Date().time) / (1000 * 60)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class RealtimeStopArea(
|
||||||
|
val lineId: String,
|
||||||
|
val lineCode: String,
|
||||||
|
val lineColors: LineColors,
|
||||||
|
val routeId: String,
|
||||||
|
val routeName: String,
|
||||||
|
val lineIsTAD: Boolean,
|
||||||
|
val nextPassages: Array<NextPassage>,
|
||||||
|
val summaryStatus: String?
|
||||||
|
) {
|
||||||
|
fun timeRemaining(): Long? =
|
||||||
|
nextPassages.getOrNull(0)?.let {
|
||||||
|
(it.date!!.time - Date().time) / (1000 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun timeRemainingAfter(): Long? =
|
||||||
|
nextPassages.getOrNull(1)?.let {
|
||||||
|
(it.date!!.time - Date().time) / (1000 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as RealtimeStopArea
|
||||||
|
|
||||||
|
if (lineId != other.lineId) return false
|
||||||
|
if (lineCode != other.lineCode) return false
|
||||||
|
if (lineColors != other.lineColors) return false
|
||||||
|
if (routeId != other.routeId) return false
|
||||||
|
if (routeName != other.routeName) return false
|
||||||
|
if (lineIsTAD != other.lineIsTAD) return false
|
||||||
|
if (!nextPassages.contentEquals(other.nextPassages)) return false
|
||||||
|
if (summaryStatus != other.summaryStatus) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = lineId.hashCode()
|
||||||
|
result = 31 * result + lineCode.hashCode()
|
||||||
|
result = 31 * result + lineColors.hashCode()
|
||||||
|
result = 31 * result + routeId.hashCode()
|
||||||
|
result = 31 * result + routeName.hashCode()
|
||||||
|
result = 31 * result + lineIsTAD.hashCode()
|
||||||
|
result = 31 * result + nextPassages.contentHashCode()
|
||||||
|
result = 31 * result + (summaryStatus?.hashCode() ?: 0)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class NextPassage(
|
||||||
|
@Serializable(with = DateAsStringSerializer::class)
|
||||||
|
val date: Date?,
|
||||||
|
val vehicleId: String,
|
||||||
|
val loadPrediction: String?, // TODO
|
||||||
|
val isLiveData: Boolean
|
||||||
|
)
|
||||||
|
|
|
@ -3,6 +3,7 @@ package fr.oupson.common.api
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.android.*
|
import io.ktor.client.engine.android.*
|
||||||
import io.ktor.client.features.json.*
|
import io.ktor.client.features.json.*
|
||||||
|
import io.ktor.client.features.json.serializer.*
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.client.request.forms.*
|
import io.ktor.client.request.forms.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
@ -12,7 +13,9 @@ import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
class TaoRestApi(private val httpClient: HttpClient) {
|
class TaoRestApi(private val httpClient: HttpClient) {
|
||||||
constructor() : this(HttpClient(Android) {
|
constructor() : this(HttpClient(Android) {
|
||||||
install(JsonFeature)
|
install(JsonFeature) {
|
||||||
|
serializer = KotlinxSerializer(kotlinx.serialization.json.Json { ignoreUnknownKeys = true })
|
||||||
|
}
|
||||||
engine {
|
engine {
|
||||||
connectTimeout = 10_1000
|
connectTimeout = 10_1000
|
||||||
socketTimeout = 10_000
|
socketTimeout = 10_000
|
||||||
|
@ -69,9 +72,24 @@ class TaoRestApi(private val httpClient: HttpClient) {
|
||||||
}, true) {}
|
}, true) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getRealtimeByStopArea(stopAreaId: String, limit: Int? = null, includeLoadPrediction: Boolean? = null) : Array<RealtimeStopArea> = withContext(Dispatchers.IO) {
|
||||||
|
httpClient.submitForm("https://navigorleans.c-t.io/api/3.0/realtime/byStopArea", Parameters.build {
|
||||||
|
append(
|
||||||
|
"stopAreaId", stopAreaId
|
||||||
|
)
|
||||||
|
if (limit != null) {
|
||||||
|
append("limit", limit.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (includeLoadPrediction != null) {
|
||||||
|
append("includeLoadPrediction", includeLoadPrediction.toString())
|
||||||
|
}
|
||||||
|
}, true) {}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun getTaoGeoJson(
|
suspend fun getTaoGeoJson(
|
||||||
lineId: String
|
lineId: String
|
||||||
): String = withContext(requestContext) {
|
): String = withContext(requestContext) {
|
||||||
httpClient.get("$baseUrl/2.0/lines/$lineId/geojson")
|
httpClient.get("$baseUrl/2.0/lines/$lineId/geojson/encoded")
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
|
||||||
|
|
|
@ -33,16 +33,15 @@ android {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.4.2'
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.4.2'
|
||||||
implementation 'androidx.core:core-ktx:1.3.2'
|
implementation 'androidx.core:core-ktx:1.6.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||||
implementation 'com.google.android.material:material:1.3.0'
|
implementation 'com.google.android.material:material:1.4.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13.2'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||||
implementation 'androidx.wear:wear:1.1.0'
|
implementation 'androidx.wear:wear:1.1.0'
|
||||||
implementation "androidx.wear:wear-tiles:1.0.0-alpha01"
|
implementation "androidx.wear:wear-tiles:1.0.0-alpha01"
|
||||||
debugImplementation "androidx.wear:wear-tiles-renderer:1.0.0-alpha01"
|
debugImplementation "androidx.wear:wear-tiles-renderer:1.0.0-alpha01"
|
||||||
|
|
Loading…
Reference in New Issue