Fix widget not working and update deps

This commit is contained in:
oupson 2022-07-10 16:41:29 +02:00
parent 6e8660a7be
commit b2140c3d42
12 changed files with 142 additions and 97 deletions

3
.gitignore vendored
View File

@ -51,6 +51,7 @@ captures/
.idea/modules.xml .idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you # Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml .idea/navEditor.xml
.idea/misc.xml
# Keystore files # Keystore files
# Uncomment the following lines if you do not want to check your keystore files in. # Uncomment the following lines if you do not want to check your keystore files in.
@ -87,4 +88,4 @@ lint/tmp/
# lint/reports/ # lint/reports/
# Android Profiling # Android Profiling
*.hprof *.hprof

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>

View File

@ -4,15 +4,15 @@ plugins {
} }
android { android {
compileSdkVersion 31 compileSdkVersion 32
buildToolsVersion "30.0.3" buildToolsVersion "30.0.3"
defaultConfig { defaultConfig {
applicationId "fr.oupson.taotoolbox" applicationId "fr.oupson.taotoolbox"
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 31 targetSdkVersion 32
versionCode 1 versionCode 1
versionName "0.0.3" versionName "0.0.4"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
} }
@ -43,18 +43,18 @@ android {
} }
dependencies { dependencies {
implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.5.0' implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.3' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.preference:preference-ktx:1.2.0'
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.2'
implementation project(":common") implementation project(":common")
implementation 'org.osmdroid:osmdroid-android:6.1.10' implementation 'org.osmdroid:osmdroid-android:6.1.13'
} }

View File

@ -292,6 +292,7 @@ class TaoWidgetConfigurationActivity : AppCompatActivity() {
super.onDestroy() super.onDestroy()
if (BuildConfig.DEBUG) if (BuildConfig.DEBUG)
Log.v(TAG, "Closing database") Log.v(TAG, "Closing database")
db?.close() db?.close()
} }

View File

@ -11,8 +11,9 @@ 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
import fr.oupson.common.api.RealTimes import fr.oupson.common.api.RealtimeStopArea
import fr.oupson.common.api.TaoRestApi import fr.oupson.common.api.TaoRestApi
import fr.oupson.taotoolbox.BuildConfig
import fr.oupson.taotoolbox.R import fr.oupson.taotoolbox.R
import fr.oupson.taotoolbox.utils.PrefsWidgetHelper import fr.oupson.taotoolbox.utils.PrefsWidgetHelper
import kotlinx.coroutines.* import kotlinx.coroutines.*
@ -28,7 +29,8 @@ class TaoWidget : AppWidgetProvider() {
appWidgetId: Int, appWidgetId: Int,
taoRestApi: TaoRestApi taoRestApi: TaoRestApi
) { ) {
Log.d(TAG, "updateAppWidget, appWidgetId : $appWidgetId") if (BuildConfig.DEBUG)
Log.d(TAG, "updateAppWidget, appWidgetId : $appWidgetId")
// Construct the RemoteViews object // Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.widget_tao) val views = RemoteViews(context.packageName, R.layout.widget_tao)
@ -56,7 +58,8 @@ class TaoWidget : AppWidgetProvider() {
val prefs = PrefsWidgetHelper.getPrefs(context, appWidgetId)!! // Caught val prefs = PrefsWidgetHelper.getPrefs(context, appWidgetId)!! // Caught
val depart = taoRestApi.getNextDeparturesForLine( val depart = taoRestApi.getNextDeparturesForLine(
prefs.stopId, prefs.stopId,
prefs.routeId prefs.routeId,
prefs.lineId,
) )
views.setRealtime( views.setRealtime(
@ -132,7 +135,7 @@ class TaoWidget : AppWidgetProvider() {
stopName: String, stopName: String,
lineName: String, lineName: String,
lineBtm: Bitmap, lineBtm: Bitmap,
realTimes: RealTimes realTimes: RealtimeStopArea
) { ) {
setImageViewBitmap( setImageViewBitmap(
R.id.widget_tao_line_icon_image_view, R.id.widget_tao_line_icon_image_view,
@ -154,7 +157,7 @@ class TaoWidget : AppWidgetProvider() {
setTextViewText( setTextViewText(
R.id.widget_tao_next_text_view, context.getString( R.id.widget_tao_next_text_view, context.getString(
R.string.time_remaining, R.string.time_remaining,
realTimes.timeRemaining(0) ?: "" realTimes.timeRemaining() ?: ""
) )
) )
@ -162,7 +165,7 @@ class TaoWidget : AppWidgetProvider() {
R.id.widget_tao_after_next_text_view, R.id.widget_tao_after_next_text_view,
context.getString( context.getString(
R.string.time_remaining, R.string.time_remaining,
realTimes.timeRemainingAfter(0) ?: "" realTimes.timeRemainingAfter() ?: ""
) )
) )
@ -170,7 +173,7 @@ class TaoWidget : AppWidgetProvider() {
R.id.widget_tao_third_text_view, R.id.widget_tao_third_text_view,
context.getString( context.getString(
R.string.time_remaining, R.string.time_remaining,
realTimes.timeRemainingThird(0) ?: "" realTimes.timeRemainingThird() ?: ""
) )
) )
} }
@ -209,10 +212,12 @@ class TaoWidget : AppWidgetProvider() {
appWidgetIds: IntArray appWidgetIds: IntArray
) { ) {
super.onUpdate(context, appWidgetManager, appWidgetIds) super.onUpdate(context, appWidgetManager, appWidgetIds)
Log.d( if (BuildConfig.DEBUG)
TAG, Log.d(
"onUpdate : appWidgetIds : ${appWidgetIds.contentToString()}" TAG,
) "onUpdate : appWidgetIds : ${appWidgetIds.contentToString()}"
)
widgetScope.launch { widgetScope.launch {
val taoRestApi = TaoRestApi().also { val taoRestApi = TaoRestApi().also {
it.requestContext = this.coroutineContext it.requestContext = this.coroutineContext
@ -226,7 +231,9 @@ class TaoWidget : AppWidgetProvider() {
override fun onDeleted(context: Context?, appWidgetIds: IntArray?) { override fun onDeleted(context: Context?, appWidgetIds: IntArray?) {
super.onDeleted(context, appWidgetIds) super.onDeleted(context, appWidgetIds)
Log.d(TAG, "onDeleted : ${appWidgetIds?.contentToString()}")
if (BuildConfig.DEBUG)
Log.d(TAG, "onDeleted : ${appWidgetIds?.contentToString()}")
runBlocking { runBlocking {
job.cancelAndJoin() job.cancelAndJoin()

View File

@ -4,13 +4,25 @@ import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.util.Log import android.util.Log
import androidx.core.content.edit import androidx.core.content.edit
import fr.oupson.taotoolbox.BuildConfig
class PrefsWidgetHelper { class PrefsWidgetHelper {
data class WidgetPrefs(
val lineId: String,
val lineCode: String,
val routeId: String,
val routeName: String,
val stopId: String,
val stopName: String,
val bgColor: Int,
val textColor: Int
)
companion object { companion object {
private const val LINE_ID = "WIDGET_LINE_ID" private const val LINE_ID = "WIDGET_LINE_ID"
private const val LINE_CODE = "WIDGET_LINE_CODE" private const val LINE_CODE = "WIDGET_LINE_CODE"
private const val ROUTE_ID = "WIDGET_LINE_ID" private const val ROUTE_ID = "WIDGET_ROUTE_ID"
private const val ROUTE_NAME = "ROUTE_NAME" private const val ROUTE_NAME = "WIDGET_ROUTE_NAME"
private const val STOP_ID = "WIDGET_STOP_ID" private const val STOP_ID = "WIDGET_STOP_ID"
private const val STOP_NAME = "WIDGET_STOP_NAME" private const val STOP_NAME = "WIDGET_STOP_NAME"
private const val BG_COLOR = "WIDGET_LINE_BACKGROUND_COLOR" private const val BG_COLOR = "WIDGET_LINE_BACKGROUND_COLOR"
@ -32,7 +44,9 @@ class PrefsWidgetHelper {
bgColor: Int, bgColor: Int,
textColor: Int textColor: Int
) { ) {
Log.d(TAG, "savePrefs, widget id : $widgetId") if (BuildConfig.DEBUG)
Log.d(TAG, "savePrefs, widget id : $widgetId")
context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE).edit(true) { context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE).edit(true) {
putString("${LINE_ID}_$widgetId", lineId) putString("${LINE_ID}_$widgetId", lineId)
putString("${LINE_CODE}_$widgetId", lineCode) putString("${LINE_CODE}_$widgetId", lineCode)
@ -45,20 +59,11 @@ class PrefsWidgetHelper {
} }
} }
data class WidgetPrefs(
val lineId: String,
val lineCode: String,
val routeId: String,
val routeName: String,
val stopId: String,
val stopName: String,
val bgColor: Int,
val textColor: Int
)
fun getPrefs(context: Context, widgetId: Int): WidgetPrefs? = fun getPrefs(context: Context, widgetId: Int): WidgetPrefs? =
with(context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)) { with(context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)) {
Log.d(TAG, "getPrefs, widget id : $widgetId") if (BuildConfig.DEBUG)
Log.d(TAG, "getPrefs, widget id : $widgetId")
WidgetPrefs( WidgetPrefs(
getString("${LINE_ID}_$widgetId", null) ?: return null, getString("${LINE_ID}_$widgetId", null) ?: return null,
getString("${LINE_CODE}_$widgetId", null) ?: return null, getString("${LINE_CODE}_$widgetId", null) ?: return null,
@ -72,7 +77,9 @@ class PrefsWidgetHelper {
} }
fun destroyPrefs(context: Context, widgetId: Int) { fun destroyPrefs(context: Context, widgetId: Int) {
Log.d(TAG, "destroyPrefs, widget id : $widgetId") if (BuildConfig.DEBUG)
Log.d(TAG, "destroyPrefs, widget id : $widgetId")
val prefs = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) val prefs = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)
prefs.edit(commit = true) { prefs.edit(commit = true) {
remove("${LINE_ID}_$widgetId") remove("${LINE_ID}_$widgetId")

View File

@ -19,6 +19,7 @@ class StopInfoWindow(
) : InfoWindow(R.layout.window_stop_info, mapView) { ) : InfoWindow(R.layout.window_stop_info, mapView) {
private val job = Job() private val job = Job()
private val windowsContext = Dispatchers.IO + job private val windowsContext = Dispatchers.IO + job
private val scope = CoroutineScope(windowsContext)
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) }
@ -37,20 +38,21 @@ class StopInfoWindow(
} }
override fun onOpen(item: Any?) { override fun onOpen(item: Any?) {
val size = realtimeList.get()?.size ?: 0
realtimeList.set(null) realtimeList.set(null)
recyclerView.adapter?.notifyDataSetChanged() recyclerView.adapter?.notifyItemRangeRemoved(0, size)
if (item is Marker) { if (item is Marker) {
val id = item.id val id = item.id
titleTextView.text = item.title titleTextView.text = item.title
GlobalScope.launch(windowsContext) { scope.launch(windowsContext) {
try { try {
val realtime = taoRestApi.getRealtimeByStopArea(id) val realtime = taoRestApi.getRealtimeByStopArea(id)
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
realtimeList.set(realtime) realtimeList.set(realtime)
adapter.notifyDataSetChanged() adapter.notifyItemRangeInserted(0, realtime.size)
} }
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
@ -66,5 +68,6 @@ class StopInfoWindow(
override fun onClose() { override fun onClose() {
windowsContext.cancelChildren() windowsContext.cancelChildren()
scope.cancel()
} }
} }

View File

@ -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.6.10" ext.kotlin_version = "1.7.0"
ext.ktor_version = "1.6.3" ext.ktor_version = "2.0.3"
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.1.1' classpath 'com.android.tools.build:gradle:7.1.3'
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

View File

@ -18,29 +18,35 @@ android {
buildTypes { buildTypes {
release { release {
//minifyEnabled false // minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
} }
dependencies { dependencies {
implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.core:core-ktx:1.8.0'
implementation 'androidx.appcompat:appcompat:1.4.1' implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.5.0' implementation 'com.google.android.material:material:1.6.1'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
implementation("io.ktor:ktor-client-content-negotiation:$ktor_version")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version")
implementation("io.ktor:ktor-client-logging:$ktor_version")
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-android:$ktor_version"
testImplementation 'junit:junit:4.13.2' testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2"
implementation "io.ktor:ktor-client-serialization:$ktor_version"
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-android:$ktor_version"
} }

View File

@ -190,6 +190,11 @@ data class RealtimeStopArea(
(it.date!!.time - Date().time) / (1000 * 60) (it.date!!.time - Date().time) / (1000 * 60)
} }
fun timeRemainingThird(): Long? =
nextPassages.getOrNull(2)?.let {
(it.date!!.time - Date().time) / (1000 * 60)
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
if (javaClass != other?.javaClass) return false if (javaClass != other?.javaClass) return false

View File

@ -1,20 +1,28 @@
package fr.oupson.common.api package fr.oupson.common.api
import io.ktor.client.* import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.android.* import io.ktor.client.engine.android.*
import io.ktor.client.features.json.* import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.features.json.serializer.* import io.ktor.client.plugins.logging.*
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.client.statement.*
import io.ktor.http.* import io.ktor.http.*
import io.ktor.serialization.kotlinx.json.*
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext 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(ContentNegotiation) {
serializer = KotlinxSerializer(kotlinx.serialization.json.Json { ignoreUnknownKeys = true }) json(kotlinx.serialization.json.Json { ignoreUnknownKeys = true })
}
install(Logging) {
logger = Logger.SIMPLE
level = LogLevel.INFO
} }
engine { engine {
connectTimeout = 10_1000 connectTimeout = 10_1000
@ -29,12 +37,13 @@ class TaoRestApi(private val httpClient: HttpClient) {
suspend fun getNextDeparturesForLine( suspend fun getNextDeparturesForLine(
stopId: String, stopId: String,
routeId: String, routeId: String,
lineId: String,
limit: Int = 100 limit: Int = 100
): RealTimes = withContext( ): RealtimeStopArea = withContext(
requestContext requestContext
) { ) {
httpClient.submitForm( httpClient.submitForm(
"$baseUrl/2.0/realtime/byStopAreaAndRoute", "$baseUrl/3.0/realtime/enhanced/byStopAreaAndRoute",
Parameters.build { Parameters.build {
append( append(
"stopAreaId", stopId "stopAreaId", stopId
@ -42,10 +51,12 @@ class TaoRestApi(private val httpClient: HttpClient) {
append( append(
"routeId", routeId "routeId", routeId
) )
append("lineId", lineId)
append("limit", "$limit") append("limit", "$limit")
append("includeLoadPrediction", "false")
}, },
true true
) {} ) {}.body()
} }
suspend fun getTaoLineJson( suspend fun getTaoLineJson(
@ -61,7 +72,7 @@ class TaoRestApi(private val httpClient: HttpClient) {
append("clientLastUpdate", clientLastUpdate ?: "1970-01-01T00:00:00.000Z") append("clientLastUpdate", clientLastUpdate ?: "1970-01-01T00:00:00.000Z")
}, },
true true
) {} ) {}.bodyAsText()
} }
suspend fun getSchedule(stopId: String): Array<Schedule> = withContext(Dispatchers.IO) { suspend fun getSchedule(stopId: String): Array<Schedule> = withContext(Dispatchers.IO) {
@ -69,27 +80,35 @@ class TaoRestApi(private val httpClient: HttpClient) {
append( append(
"stopId", stopId "stopId", stopId
) )
}, true) {} }, true) {}.body()
} }
suspend fun getRealtimeByStopArea(stopAreaId: String, limit: Int? = null, includeLoadPrediction: Boolean? = null) : Array<RealtimeStopArea> = withContext(Dispatchers.IO) { suspend fun getRealtimeByStopArea(
httpClient.submitForm("https://navigorleans.c-t.io/api/3.0/realtime/byStopArea", Parameters.build { stopAreaId: String,
append( limit: Int? = null,
"stopAreaId", stopAreaId includeLoadPrediction: Boolean? = null
) ): Array<RealtimeStopArea> = withContext(Dispatchers.IO) {
if (limit != null) { httpClient.submitForm(
append("limit", limit.toString()) "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) { if (includeLoadPrediction != null) {
append("includeLoadPrediction", includeLoadPrediction.toString()) append("includeLoadPrediction", includeLoadPrediction.toString())
} }
}, true) {} },
true
) {}.body()
} }
suspend fun getTaoGeoJson( suspend fun getTaoGeoJson(
lineId: String lineId: String
): String = withContext(requestContext) { ): String = withContext(requestContext) {
httpClient.get("$baseUrl/2.0/lines/$lineId/geojson/encoded") httpClient.get("$baseUrl/2.0/lines/$lineId/geojson/encoded").bodyAsText()
} }
} }

View File

@ -4,13 +4,13 @@ plugins {
} }
android { android {
compileSdkVersion 30 compileSdkVersion 31
buildToolsVersion "30.0.3" buildToolsVersion "30.0.3"
defaultConfig { defaultConfig {
applicationId "fr.oupson.wear_tao" applicationId "fr.oupson.wear_tao"
minSdkVersion 26 minSdkVersion 26
targetSdkVersion 30 targetSdkVersion 31
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
@ -23,28 +23,33 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
kotlinOptions { kotlinOptions {
jvmTarget = '1.8' jvmTarget = '1.8'
} }
} }
dependencies { dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0' implementation 'androidx.core:core-ktx:1.8.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.4.2' implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'androidx.core:core-ktx:1.6.0' implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.0' implementation 'androidx.wear:wear:1.2.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.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"
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.0'
implementation project(":common") implementation project(":common")
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
} }