From eb9e0245bd38abf79d2cefcdf66b8ecbf46fc73b Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Sun, 23 Oct 2022 03:07:23 +0900 Subject: [PATCH 01/23] build.gradle update --- app/build.gradle | 78 +++---- .../main/java/kr/yhs/traffic/MainActivity.kt | 23 +- .../kr/yhs/traffic/SharedPreferencesClient.kt | 204 ------------------ .../main/java/kr/yhs/traffic/ui/ComposeApp.kt | 164 ++++++++------ .../main/res/xml/data_extraction_rules.xml | 2 +- app/src/main/res/xml/full_backup_content.xml | 2 +- build.gradle | 13 +- gradle/libs.versions.toml | 59 +++++ settings.gradle | 3 + 9 files changed, 224 insertions(+), 324 deletions(-) delete mode 100644 app/src/main/java/kr/yhs/traffic/SharedPreferencesClient.kt create mode 100644 gradle/libs.versions.toml diff --git a/app/build.gradle b/app/build.gradle index f7ff552..55d3c1b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "kr.yhs.traffic" minSdk 26 targetSdk 33 - versionCode 1111 - versionName "1.1.1" + versionCode 1112 + versionName "1.1.2" } buildTypes { release { @@ -22,7 +22,7 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion "1.2.0" + kotlinCompilerExtensionVersion libs.versions.compose.get() } compileOptions { sourceCompatibility JavaVersion.VERSION_11 @@ -39,51 +39,51 @@ android { } dependencies { - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'com.google.android.gms:play-services-wearable:18.0.0' - implementation 'androidx.percentlayout:percentlayout:1.0.0' - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.wear:wear:1.2.0' - implementation 'androidx.wear:wear-input:1.2.0-alpha02' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' - implementation 'androidx.security:security-crypto-ktx:1.1.0-alpha03' + implementation(libs.androidx.coreKtx) + implementation(libs.percentlayout) + implementation(libs.legacy) + implementation(libs.lifecycle) + implementation(libs.security) + + // Wearable + implementation(libs.wear) + implementation(libs.wear.input) + + // Google Play Services + implementation(libs.google.gms.wearable) + implementation(libs.google.gms.location) // Coroutines - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.6.4' - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.4' + implementation(libs.coroutines.android) + implementation(libs.coroutines.core) + implementation(libs.coroutines.guava) + implementation(libs.coroutines.playServices) - // accompanist pager - implementation 'com.google.accompanist:accompanist-pager:0.24.6-alpha' - implementation 'com.google.accompanist:accompanist-pager-indicators:0.24.6-alpha' - implementation 'com.google.accompanist:accompanist-permissions:0.24.6-alpha' + // Accompaist Pager & Permission + implementation(libs.accompaist.pager) + implementation(libs.accompaist.pagerIndicators) + implementation(libs.accompaist.permissions) // Jetpack Compose - implementation "androidx.activity:activity-compose:1.6.0" - implementation "androidx.compose.ui:ui-tooling-preview:1.3.0-rc01" - implementation "androidx.compose.foundation:foundation:1.3.0-rc01" - implementation "androidx.compose.material:material-icons-extended:1.3.0-rc01" - - // Jetpack Compose (Wearable) - implementation "androidx.wear.compose:compose-material:1.1.0-alpha07" - implementation "androidx.wear.compose:compose-foundation:1.1.0-alpha07" - implementation "androidx.wear.compose:compose-navigation:1.1.0-alpha07" + implementation(libs.androidx.activity.compose) + implementation(libs.compose.uiToolingPreview) + implementation(libs.compose.foundation) + implementation(libs.compose.materialIconExtended) - // GMS Location - implementation 'com.google.android.gms:play-services-location:21.0.0' + // Wear Jetpack Compose + implementation(libs.compose.wear.material) + implementation(libs.compose.wear.foundation) + implementation(libs.compose.wear.navigation) // Retrofit - implementation 'com.squareup.retrofit2:retrofit:2.9.0' - implementation 'com.squareup.retrofit2:converter-gson:2.9.0' - implementation 'com.squareup.retrofit2:converter-scalars:2.9.0' + implementation(libs.retrofit.core) + implementation(libs.retrofit.converter.gson) + implementation(libs.retrofit.converter.scalars) // For Testing - testImplementation "junit:junit:4.13.2" - androidTestImplementation "androidx.test.ext:junit:1.1.3" - androidTestImplementation "androidx.test.espresso:espresso-core:3.4.0" - androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.3.0-rc01" - debugImplementation "androidx.compose.ui:ui-tooling:1.3.0-rc01" + testImplementation(libs.bundles.tests) + androidTestImplementation(libs.bundles.androidTest) + debugImplementation(libs.bundles.debugTest) - compileOnly 'com.google.android.wearable:wearable:2.9.0' + compileOnly(libs.wearable) } \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/MainActivity.kt b/app/src/main/java/kr/yhs/traffic/MainActivity.kt index fe2e840..7106c4e 100644 --- a/app/src/main/java/kr/yhs/traffic/MainActivity.kt +++ b/app/src/main/java/kr/yhs/traffic/MainActivity.kt @@ -1,10 +1,13 @@ package kr.yhs.traffic +import android.content.SharedPreferences import android.content.pm.PackageManager import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices import kr.yhs.traffic.module.TrafficClient @@ -16,10 +19,20 @@ import retrofit2.converter.gson.GsonConverterFactory class MainActivity : ComponentActivity() { var fusedLocationClient: FusedLocationProviderClient? = null var client: TrafficClient? = null - var spClient: SharedPreferencesClient? = null + lateinit var masterKey: MasterKey + + fun getPreferences(filename: String): SharedPreferences = + EncryptedSharedPreferences.create( + this, filename, masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + masterKey = MasterKey.Builder(this.baseContext) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() val httpClient = OkHttpClient.Builder() val retrofit = Retrofit.Builder() .baseUrl("https://api.yhs.kr") @@ -28,14 +41,6 @@ class MainActivity : ComponentActivity() { .build() client = retrofit.create(TrafficClient::class.java) - spClient = SharedPreferencesClient("traffic", this) - val versionCode = spClient!!.getInt("versionCode", default = 1005) - if (versionCode < 1006) { - // Conflict Prevention by Version Update - spClient!!.clear() - spClient!!.setInt("versionCode", 1006) - } - setContent { ComposeApp(this).Content() } diff --git a/app/src/main/java/kr/yhs/traffic/SharedPreferencesClient.kt b/app/src/main/java/kr/yhs/traffic/SharedPreferencesClient.kt deleted file mode 100644 index 19dcdfa..0000000 --- a/app/src/main/java/kr/yhs/traffic/SharedPreferencesClient.kt +++ /dev/null @@ -1,204 +0,0 @@ -package kr.yhs.traffic - -import android.content.Context -import android.content.SharedPreferences -import androidx.core.content.edit -import org.json.JSONArray - - -class SharedPreferencesClient(private val preferencesName: String, private val context: Context) { - /* var masterKey = MasterKey.Builder(context) - .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) - .build(); */ - - private fun getPreferences(): SharedPreferences = - /* EncryptedSharedPreferences.create( - context, - preferencesName, - masterKey, - EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, - EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM - ) */ - context.getSharedPreferences(preferencesName, Context.MODE_PRIVATE) // Sensi - - fun setString(key: String, value: String?) { - val prefs = getPreferences() - prefs.apply { - edit { - putString(key, value) - commit() - } - } - } - - fun setBoolean(key: String, value: Boolean) { - val prefs = getPreferences() - prefs.apply { - edit { - putBoolean(key, value) - commit() - } - } - } - - fun setInt(key: String, value: Int) { - val prefs = getPreferences() - prefs.apply { - edit { - putInt(key, value) - commit() - } - } - } - - fun setFloat(key: String, value: Float) { - val prefs = getPreferences() - prefs.apply { - edit { - putFloat(key, value) - commit() - } - } - } - - fun getString( - key: String, - default: String? = null - ): String? { - val prefs = getPreferences() - return prefs.getString(key, default) - } - - fun getBoolean( - key: String, - default: Boolean = false - ): Boolean { - val prefs = getPreferences() - return prefs.getBoolean(key, default) - } - - fun getInt( - key: String, - default: Int = 0 - ): Int { - val prefs = getPreferences() - return prefs.getInt( - key, - default - ) - } - - fun getFloat( - key: String, - default: Float = 0.0f - ): Float { - val prefs = getPreferences() - return prefs.getFloat( - key, - default - ) - } - - fun removeKey(key: String) { - val prefs = getPreferences() - prefs.apply { - edit { - remove(key) - commit() - } - } - } - - fun clear() { - val prefs = getPreferences() - prefs.apply { - edit { - clear() - commit() - } - } - } - - fun setArrayExtension(key: String, values: ArrayList) { - val jsonArray = JSONArray() - for (v in values) { - jsonArray.put(v) - } - if (values.isNotEmpty()) { - setString(key, jsonArray.toString()) - } else { - removeKey(key) - } - } - - fun getArrayExtension(key: String): ArrayList { - val preData = getString(key) - val arrayList = arrayListOf() - if (preData != null) { - val jsonArray = JSONArray(preData) - for (v in 0 until jsonArray.length()) { - arrayList.add( - jsonArray.opt(v) - ) - } - } - return arrayList - } - - fun setMutableType(key: String, values: Any?) { - when (values) { - is List<*> -> { - setString("$key-type", "list") - val value = arrayListOf() - for (data in values) - value.add(value) - setArrayExtension("$key-array",value) - } - is Int -> { - setString("$key-type", "int") - setInt("$key-int",values) - } - is String -> { - setString("$key-type", "string") - setString("$key-int",values) - } - is Float -> { - setString("$key-type", "float") - setFloat("$key-int",values) - } - } - } - - fun getMutableType(key: String, default: Any? = null): Any? { - val typeData = getString("$key-type") - return when (typeData) { - "list" -> { - val value = mutableListOf() - val arrayData = getArrayExtension("$key-array") - for (data in arrayData) - value.add(data) - return value - } - "int" -> getInt("$key-int") - "string" -> getInt("$key-string") - "float" -> getInt("$key-float") - else -> default - } - } - - fun removeMutableType(key: String) { - val prefs = getPreferences() - val typeData = getString("$key-type") - prefs.apply { - edit { - when (typeData) { - "list" -> remove("$key-array") - "int" -> remove("$key-int") - "string" -> remove("$key-string") - "float" -> remove("$key-float") - } - commit() - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt index dd8f59f..7fafafa 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt @@ -3,6 +3,7 @@ package kr.yhs.traffic.ui import android.Manifest import android.app.Activity import android.app.RemoteInput +import android.content.SharedPreferences import android.content.pm.PackageManager import android.location.Location import android.os.Build @@ -13,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.core.app.ActivityCompat +import androidx.core.content.edit import androidx.navigation.NavType import androidx.navigation.navArgument import androidx.wear.compose.navigation.SwipeDismissableNavHost @@ -43,7 +45,6 @@ import java.net.SocketTimeoutException class ComposeApp(private val activity: MainActivity) { private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default - private val bookmarkArrayKey = "bookmark-station" @OptIn( com.google.accompanist.pager.ExperimentalPagerApi::class, @@ -191,31 +192,23 @@ class ComposeApp(private val activity: MainActivity) { } StationListType.BOOKMARK -> { val bookmarkStation = mutableListOf() - val sharedPreferences = activity.spClient!! - val bookmarkData = sharedPreferences.getArrayExtension("bookmark-station") - for (stationId in bookmarkData) { + val sharedPreferences = activity.getPreferences("bookmark") + val bookmark = sharedPreferences.getStringSet("bookmark-list", mutableSetOf()) + bookmark?.forEach { stationId: String -> + // Log.i("Bookmark", stationId) bookmarkStation.add( StationInfo( - sharedPreferences.getString("$stationId-name") ?: activity.getString(R.string.unknown), - sharedPreferences.getString("$stationId-id") ?: "-2", - sharedPreferences.getString("$stationId-ids"), - sharedPreferences.getFloat("$stationId-posX").toDouble(), - sharedPreferences.getFloat("$stationId-posY").toDouble(), - sharedPreferences.getString( - "$stationId-displayId", - "0" - ), - sharedPreferences.getMutableType("$stationId-stationId") - ?: "0", - sharedPreferences.getInt("$stationId-type") + sharedPreferences.getString("$stationId-name", "알 수 없음") ?: "알 수 없음", + sharedPreferences.getString("$stationId-id", null) ?: "-2", + sharedPreferences.getString("$stationId-ids", null), + sharedPreferences.getFloat("$stationId-posX", 0.0F).toDouble(), + sharedPreferences.getFloat("$stationId-posY", 0.0F).toDouble(), + sharedPreferences.getString("$stationId-displayId", null) ?: "0", + getMutableType(sharedPreferences, "$stationId-stationId", null) ?: "0", + sharedPreferences.getInt("$stationId-type", 0), ) ) } - // Log.i("stationBookmark", "$bookmarkData $bookmarkStation") - sharedPreferences.setArrayExtension( - bookmarkArrayKey, - bookmarkData - ) bookmarkStation } else -> listOf() @@ -286,59 +279,51 @@ class ComposeApp(private val activity: MainActivity) { } // Log.i("BusInfo", "$busList") } - val preBookmarkData = activity.spClient!!.getArrayExtension(bookmarkArrayKey) + val sharedPreferences = activity.getPreferences("bookmark") + val bookmark = sharedPreferences.getStringSet("bookmark-list", mutableSetOf())?: mutableSetOf() val bookmarkKey = "${postLastStation.routeId}0${postLastStation.type}" - // Log.i("station-bookmark", "$preBookmarkData $bookmarkKey ${preBookmarkData.indexOf(bookmarkKey)}") + StationInfoPage( - postLastStation, - busList, - preBookmarkData.contains(bookmarkKey), - scope + postLastStation, busList, + bookmark.contains(bookmarkKey), scope ) { when (it) { StationInfoSelection.BOOKMARK -> { - val sharedPreferences = activity.spClient!! - val bookmarkData = sharedPreferences.getArrayExtension(bookmarkArrayKey) - // Log.d("station-bookmark", "$bookmarkData $bookmarkKey ${bookmarkData.indexOf(bookmarkKey)}") - if (bookmarkData.contains(bookmarkKey)) { - bookmarkData.remove(bookmarkKey) - sharedPreferences.removeKey("$bookmarkKey-name") - sharedPreferences.removeKey("$bookmarkKey-type") - sharedPreferences.removeKey("$bookmarkKey-id") - sharedPreferences.removeKey("$bookmarkKey-ids") - sharedPreferences.removeKey("$bookmarkKey-posX") - sharedPreferences.removeKey("$bookmarkKey-posY") - sharedPreferences.removeMutableType("$bookmarkKey-stationId") - sharedPreferences.removeKey("$bookmarkKey-displayId") + val newBookmark = mutableSetOf() + newBookmark.addAll(bookmark.toMutableSet()) + if (bookmark.contains(bookmarkKey)) { + listOf("name", "type", "id", "ids", "posX", "posY", "stationId", "displayId" + ).forEach { + sharedPreferences.edit { + if (sharedPreferences.contains("$bookmarkKey-$it-value")) + removeMutableType(this, "$bookmarkKey-$it") + else + remove("$bookmarkKey-$it") + } + } + newBookmark.remove(bookmarkKey) + sharedPreferences.edit { + putStringSet("bookmark-list", newBookmark) + commit() + } } else { - var displayId = postLastStation.displayId - displayId = if (displayId is List<*>) { - displayId.joinToString(", ") - } else displayId?.toString() ?: " " - - bookmarkData.add(bookmarkKey) - sharedPreferences.setString( - "$bookmarkKey-name", - postLastStation.name - ) - sharedPreferences.setInt("$bookmarkKey-type", postLastStation.type) - sharedPreferences.setString("$bookmarkKey-id", postLastStation.id) - sharedPreferences.setString("$bookmarkKey-id", postLastStation.ids) - sharedPreferences.setFloat( - "$bookmarkKey-posX", - postLastStation.posX.toFloat() - ) - sharedPreferences.setFloat( - "$bookmarkKey-posY", - postLastStation.posY.toFloat() - ) - sharedPreferences.setMutableType( - "$bookmarkKey-stationId", - postLastStation.stationId - ) - sharedPreferences.setString("$bookmarkKey-displayId", displayId) + val displayId = if (postLastStation.displayId is List<*>) { + postLastStation.displayId.joinToString(", ") + } else postLastStation.displayId?.toString() ?: " " + newBookmark.add(bookmarkKey) + sharedPreferences.edit { + putString("$bookmarkKey-name", postLastStation.name) + putInt("$bookmarkKey-type", postLastStation.type) + putString("$bookmarkKey-id", postLastStation.id) + putString("$bookmarkKey-ids", postLastStation.ids) + putFloat("$bookmarkKey-posX", postLastStation.posX.toFloat()) + putFloat("$bookmarkKey-posY", postLastStation.posY.toFloat()) + putMutableType(this, "$bookmarkKey-stationId", postLastStation.stationId) + putString("$bookmarkKey-displayId", displayId) + putStringSet("bookmark-list", newBookmark) + commit() + } } - activity.spClient!!.setArrayExtension(bookmarkArrayKey, bookmarkData) } StationInfoSelection.REFRESH -> { scope.launch { @@ -354,6 +339,51 @@ class ComposeApp(private val activity: MainActivity) { } } + private fun getMutableType(prefs: SharedPreferences, key: String, default: Any? = null): Any? { + return when (prefs.getString("$key-type", null)) { + "list" -> prefs.getStringSet("$key-value", mutableSetOf())?.toList() + "set" -> prefs.getStringSet("$key-value", mutableSetOf()) + "int" -> prefs.getInt("$key-value", 0) + "string" -> prefs.getString("$key-value", null) + "float" -> prefs.getFloat("$key-value", 0.0F) + else -> default + } + } + + private fun putMutableType(editor: SharedPreferences.Editor, key: String, values: Any) { + when { + values is List<*> && values.all { it is String } -> { + editor.putString("$key-type", "list") + val dumpSet = mutableSetOf() + values.forEach { dumpSet.add(it as String) } + editor.putStringSet("$key-value", dumpSet) + } + values is MutableSet<*> && values.all { it is String } -> { + editor.putString("$key-type", "set") + val dumpSet = mutableSetOf() + values.forEach { dumpSet.add(it as String) } + editor.putStringSet("$key-value", dumpSet) + } + values is Int -> { + editor.putString("$key-type", "int") + editor.putInt("$key-value",values) + } + values is String -> { + editor.putString("$key-type", "string") + editor.putString("$key-value",values) + } + values is Float -> { + editor.putString("$key-type", "float") + editor.putFloat("$key-value",values) + } + } + } + + private fun removeMutableType(editor: SharedPreferences.Editor, key: String) { + editor.remove("$key-type") + editor.remove("$key-value") + } + private suspend fun getStation(dispatcher: CoroutineDispatcher, query: String, cityCode: Int) = withContext(dispatcher) { activity.client!!.getStation( diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml index 0d05199..b8ba97c 100644 --- a/app/src/main/res/xml/data_extraction_rules.xml +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -6,6 +6,6 @@ --> - + \ No newline at end of file diff --git a/app/src/main/res/xml/full_backup_content.xml b/app/src/main/res/xml/full_backup_content.xml index 53f00a8..28c458e 100644 --- a/app/src/main/res/xml/full_backup_content.xml +++ b/app/src/main/res/xml/full_backup_content.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/build.gradle b/build.gradle index 0e92a69..99943da 100644 --- a/build.gradle +++ b/build.gradle @@ -1,8 +1,15 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext { + kotlinVersion = "${libs.versions.kotlin.get()}" + gradleVersion = "${libs.versions.gradle.get()}" + } +} + plugins { - id 'com.android.application' version '7.2.2' apply false - id 'com.android.library' version '7.2.2' apply false - id 'org.jetbrains.kotlin.android' version '1.7.0' apply false + id 'com.android.application' version "${gradleVersion}" apply false + id 'com.android.library' version "${gradleVersion}" apply false + id 'org.jetbrains.kotlin.android' version "${kotlinVersion}" apply false id "org.sonarqube" version "3.4.0.2513" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..2da976e --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,59 @@ +[versions] +kotlin = "1.7.10" +coroutine = "1.6.4" +gradle = "7.2.2" + +accompanist = "0.24.6-alpha" +retrofit = "2.9.0" +compose = "1.3.0-rc01" +wearCompose = "1.1.0-alpha07" + +[libraries] +coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutine" } +coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutine" } +coroutines-guava = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-guava", version.ref = "coroutine" } +coroutines-playServices = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-play-services", version.ref = "coroutine" } + +accompaist-pager = { module = "com.google.accompanist:accompanist-pager", version.ref = "accompanist" } +accompaist-pagerIndicators = { module = "com.google.accompanist:accompanist-pager-indicators", version.ref = "accompanist" } +accompaist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist" } + +compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" } +compose-material = { module = "androidx.compose.material:material", version.ref = "compose" } +compose-uiTooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } +compose-uiToolingPreview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } +compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" } +compose-materialIconExtended = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" } + +compose-wear-material = { module = "androidx.wear.compose:compose-material", version.ref = "wearCompose" } +compose-wear-foundation = { module = "androidx.wear.compose:compose-foundation", version.ref = "wearCompose" } +compose-wear-navigation = { module = "androidx.wear.compose:compose-navigation", version.ref = "wearCompose" } + +androidx-coreKtx = "androidx.core:core-ktx:1.9.0" +androidx-activity-compose = "androidx.activity:activity-compose:1.6.0" + +google-gms-wearable = "com.google.android.gms:play-services-wearable:18.0.0" +google-gms-location = "com.google.android.gms:play-services-location:21.0.0" + +percentlayout = "androidx.percentlayout:percentlayout:1.0.0" +legacy = "androidx.legacy:legacy-support-v4:1.0.0" +lifecycle = "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1" +security = "androidx.security:security-crypto:1.1.0-alpha03" + +retrofit-core = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" } +retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" } +retrofit-converter-scalars = { module = "com.squareup.retrofit2:converter-scalars", version.ref = "retrofit" } + +test-junit = "junit:junit:4.13.2" +test-ext = "androidx.test.ext:junit:1.1.3" +compose-ui-test = { module = "androidx.compose.ui:ui-test-junit4", version.ref = "compose" } +espresso = "androidx.test.espresso:espresso-core:3.4.0" + +wearable = "com.google.android.wearable:wearable:2.9.0" +wear = "androidx.wear:wear:1.2.0" +wear-input = "androidx.wear:wear-input:1.2.0-alpha02" + +[bundles] +tests = ["test-junit"] +androidTest = ["test-ext", "compose-ui-test", "espresso"] +debugTest = ["compose-uiTooling"] \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 69ac213..b257555 100644 --- a/settings.gradle +++ b/settings.gradle @@ -5,9 +5,12 @@ pluginManagement { mavenCentral() } } +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +enableFeaturePreview("VERSION_CATALOGS") dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { + gradlePluginPortal() google() mavenCentral() } From 7a8552077e4f35b93d398d92eed009cb109b6ffa Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Mon, 24 Oct 2022 02:17:07 +0900 Subject: [PATCH 02/23] code clean & add ArrivingSoonTileService.kt --- app/build.gradle | 2 + app/src/main/AndroidManifest.xml | 33 +- .../traffic/tiles/ArrivingSoonTileService.kt | 17 + .../main/java/kr/yhs/traffic/ui/ComposeApp.kt | 409 ++++++++++-------- app/src/main/res/values-en/strings.xml | 3 + app/src/main/res/values/strings.xml | 3 + gradle/libs.versions.toml | 10 +- 7 files changed, 295 insertions(+), 182 deletions(-) create mode 100644 app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt diff --git a/app/build.gradle b/app/build.gradle index 55d3c1b..f4eddb9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,6 +48,8 @@ dependencies { // Wearable implementation(libs.wear) implementation(libs.wear.input) + implementation(libs.wear.tile) + implementation(libs.wear.tile.material) // Google Play Services implementation(libs.google.gms.wearable) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a16871f..4cae114 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -48,6 +48,37 @@ - + + + + + \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt new file mode 100644 index 0000000..b13ae99 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt @@ -0,0 +1,17 @@ +package kr.yhs.traffic.tiles + +import androidx.wear.tiles.RequestBuilders +import androidx.wear.tiles.ResourceBuilders +import androidx.wear.tiles.TileBuilders +import androidx.wear.tiles.TileService +import com.google.common.util.concurrent.ListenableFuture + +class ArrivingSoonTileService: TileService() { + override fun onTileRequest(requestParams: RequestBuilders.TileRequest): ListenableFuture { + TODO("Not yet implemented") + } + + override fun onResourcesRequest(requestParams: RequestBuilders.ResourcesRequest): ListenableFuture { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt index 7fafafa..869946b 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt @@ -7,7 +7,6 @@ import android.content.SharedPreferences import android.content.pm.PackageManager import android.location.Location import android.os.Build -import android.util.Log import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.fillMaxSize @@ -15,6 +14,7 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.core.app.ActivityCompat import androidx.core.content.edit +import androidx.navigation.NavController import androidx.navigation.NavType import androidx.navigation.navArgument import androidx.wear.compose.navigation.SwipeDismissableNavHost @@ -23,10 +23,7 @@ import androidx.wear.compose.navigation.rememberSwipeDismissableNavController import androidx.wear.input.RemoteInputIntentHelper import androidx.wear.widget.ConfirmationOverlay import com.google.accompanist.permissions.rememberMultiplePermissionsState -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import kr.yhs.traffic.MainActivity import kr.yhs.traffic.R import kr.yhs.traffic.models.DropdownQuery @@ -62,7 +59,8 @@ class ComposeApp(private val activity: MainActivity) { if (it.resultCode == Activity.RESULT_OK) { val intent = it.data val remoteInputResponse = RemoteInput.getResultsFromIntent(intent) - stationQuery = remoteInputResponse.getCharSequence("SEARCH_BUS_STATION", "").toString() + stationQuery = + remoteInputResponse.getCharSequence("SEARCH_BUS_STATION", "").toString() navigationController.navigate( Screen.StationList.route + "?$STATION_TYPE=${StationListType.SEARCH}", ) @@ -76,22 +74,13 @@ class ComposeApp(private val activity: MainActivity) { ) { composable(Screen.MainScreen.route) { MainPage( - scope, - listOf({ - StationSearch( - activity.getString(R.string.station_search_title), - activity.getString(R.string.station_search_description), - items = listOf( - DropdownQuery(activity.getString(R.string.item_metropolitan), 1), - DropdownQuery(activity.getString(R.string.item_buc), 3) - ) - ) { cityCode: Int -> + scope = scope, + pages = listOf({ + this@ComposeApp.StationSearch(navigationController) { cityCode: Int -> queryCityCode = cityCode val remoteInputs = listOf( RemoteInput.Builder("SEARCH_BUS_STATION") - .setLabel( - activity.getString(R.string.search_label) - ) + .setLabel(activity.getString(R.string.search_label)) .build() ) val intent = RemoteInputIntentHelper.createActionRemoteInputIntent() @@ -99,25 +88,10 @@ class ComposeApp(private val activity: MainActivity) { launcher.launch(intent) } }, { - StationGPS( - activity.getString(R.string.station_gps_title), - activity.getString(R.string.station_gps_description) - ) { - navigationController.navigate( - Screen.StationList.route + "?$STATION_TYPE=${StationListType.GPS_LOCATION_SEARCH}", - ) - } + this@ComposeApp.StationGPS(navigationController) }, { - StationStar( - activity.getString(R.string.station_star_title), - activity.getString(R.string.station_star_description) - ) { - navigationController.navigate( - Screen.StationList.route + "?$STATION_TYPE=${StationListType.BOOKMARK}", - ) - } - } - ) + this@ComposeApp.StationStar(navigationController) + }) ) } composable( @@ -134,115 +108,25 @@ class ComposeApp(private val activity: MainActivity) { } else { it.arguments?.getSerializable(STATION_TYPE) } - var stationList by remember { mutableStateOf>(emptyList()) } - var location by remember { mutableStateOf(null) } - - val permissionResult = rememberMultiplePermissionsState( - listOf( - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION - ) - ) - LaunchedEffect(true) { - if (activity.fusedLocationClient != null) { - if (ActivityCompat.checkSelfPermission( - activity, Manifest.permission.ACCESS_FINE_LOCATION - ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( - activity, Manifest.permission.ACCESS_COARSE_LOCATION - ) != PackageManager.PERMISSION_GRANTED - ) { - permissionResult.launchMultiplePermissionRequest() - if (!permissionResult.allPermissionsGranted) { - ConfirmationOverlay() - .setType(ConfirmationOverlay.FAILURE_ANIMATION) - .setMessage(activity.getText(R.string.gps_permission)) - .showOn(activity) - navigationController.popBackStack() - return@LaunchedEffect - } - } - location = getLocation( - activity.fusedLocationClient!!, - stationType == StationListType.GPS_LOCATION_SEARCH - ) - } - Log.i("location", "$location") - if (location == null && stationType == StationListType.GPS_LOCATION_SEARCH) { - ConfirmationOverlay() - .setType(ConfirmationOverlay.FAILURE_ANIMATION) - .setMessage(activity.getText(R.string.gps_not_found)) - .showOn(activity) - navigationController.popBackStack() - return@LaunchedEffect - } - try { - stationList = when (stationType) { - StationListType.SEARCH -> { - getStation(defaultDispatcher, stationQuery, queryCityCode) - } - StationListType.GPS_LOCATION_SEARCH -> { - val convertData = mutableListOf() - val stationAroundList = getStationAround(defaultDispatcher, location!!.longitude, location!!.latitude) - for (st in stationAroundList) { - convertData.add( - st.convertToStationInfo() - ) - } - convertData - } - StationListType.BOOKMARK -> { - val bookmarkStation = mutableListOf() - val sharedPreferences = activity.getPreferences("bookmark") - val bookmark = sharedPreferences.getStringSet("bookmark-list", mutableSetOf()) - bookmark?.forEach { stationId: String -> - // Log.i("Bookmark", stationId) - bookmarkStation.add( - StationInfo( - sharedPreferences.getString("$stationId-name", "알 수 없음") ?: "알 수 없음", - sharedPreferences.getString("$stationId-id", null) ?: "-2", - sharedPreferences.getString("$stationId-ids", null), - sharedPreferences.getFloat("$stationId-posX", 0.0F).toDouble(), - sharedPreferences.getFloat("$stationId-posY", 0.0F).toDouble(), - sharedPreferences.getString("$stationId-displayId", null) ?: "0", - getMutableType(sharedPreferences, "$stationId-stationId", null) ?: "0", - sharedPreferences.getInt("$stationId-type", 0), - ) - ) - } - bookmarkStation - } - else -> listOf() - } - } catch (e: SocketTimeoutException) { + this@ComposeApp.StationListPage( + stationType as StationListType, + stationQuery, + queryCityCode, + scope, + onFailed = { errorMessage: CharSequence -> ConfirmationOverlay() .setType(ConfirmationOverlay.FAILURE_ANIMATION) - .setMessage(activity.getText(R.string.timeout)) + .setMessage(errorMessage) .showOn(activity) navigationController.popBackStack() - return@LaunchedEffect + }, onSuccess = { station: StationInfo -> + lastStation = station + navigationController.navigate( + Screen.StationInfo.route, + ) } - } - if (activity.fusedLocationClient == null && stationType == StationListType.GPS_LOCATION_SEARCH) { - ConfirmationOverlay() - .setType(ConfirmationOverlay.FAILURE_ANIMATION) - .setMessage(activity.getText(R.string.gps_not_found)) - .showOn(activity) - navigationController.popBackStack() - return@composable - } + ) - val title = when (stationType) { - StationListType.SEARCH -> activity.getString(R.string.title_search, stationQuery) - StationListType.GPS_LOCATION_SEARCH -> activity.getString(R.string.title_gps_location) - StationListType.BOOKMARK -> activity.getString(R.string.title_bookmark) - else -> activity.getString(R.string.title_search) - } - StationListPage(title, stationList, location, scope) { station: StationInfo -> - lastStation = station - navigationController.navigate( - Screen.StationInfo.route, - ) - } } composable( Screen.StationInfo.route @@ -256,16 +140,16 @@ class ComposeApp(private val activity: MainActivity) { navigationController.popBackStack() return@composable } - val postLastStation = lastStation!! + val station = lastStation!! LaunchedEffect(true) { try { busList = getRoute( defaultDispatcher, - postLastStation.routeId, - postLastStation.type + station.routeId, + station.type ) } catch (e: Exception) { - when(e) { + when (e) { is SocketTimeoutException, is HttpException -> { ConfirmationOverlay() .setType(ConfirmationOverlay.FAILURE_ANIMATION) @@ -280,11 +164,13 @@ class ComposeApp(private val activity: MainActivity) { // Log.i("BusInfo", "$busList") } val sharedPreferences = activity.getPreferences("bookmark") - val bookmark = sharedPreferences.getStringSet("bookmark-list", mutableSetOf())?: mutableSetOf() - val bookmarkKey = "${postLastStation.routeId}0${postLastStation.type}" + val bookmark = + sharedPreferences.getStringSet("bookmark-list", mutableSetOf()) + ?: mutableSetOf() + val bookmarkKey = "${station.routeId}0${station.type}" StationInfoPage( - postLastStation, busList, + station, busList, bookmark.contains(bookmarkKey), scope ) { when (it) { @@ -292,7 +178,15 @@ class ComposeApp(private val activity: MainActivity) { val newBookmark = mutableSetOf() newBookmark.addAll(bookmark.toMutableSet()) if (bookmark.contains(bookmarkKey)) { - listOf("name", "type", "id", "ids", "posX", "posY", "stationId", "displayId" + listOf( + "name", + "type", + "id", + "ids", + "posX", + "posY", + "stationId", + "displayId" ).forEach { sharedPreferences.edit { if (sharedPreferences.contains("$bookmarkKey-$it-value")) @@ -307,18 +201,22 @@ class ComposeApp(private val activity: MainActivity) { commit() } } else { - val displayId = if (postLastStation.displayId is List<*>) { - postLastStation.displayId.joinToString(", ") - } else postLastStation.displayId?.toString() ?: " " + val displayId = if (station.displayId is List<*>) { + station.displayId.joinToString(", ") + } else station.displayId?.toString() ?: " " newBookmark.add(bookmarkKey) sharedPreferences.edit { - putString("$bookmarkKey-name", postLastStation.name) - putInt("$bookmarkKey-type", postLastStation.type) - putString("$bookmarkKey-id", postLastStation.id) - putString("$bookmarkKey-ids", postLastStation.ids) - putFloat("$bookmarkKey-posX", postLastStation.posX.toFloat()) - putFloat("$bookmarkKey-posY", postLastStation.posY.toFloat()) - putMutableType(this, "$bookmarkKey-stationId", postLastStation.stationId) + putString("$bookmarkKey-name", station.name) + putInt("$bookmarkKey-type", station.type) + putString("$bookmarkKey-id", station.id) + putString("$bookmarkKey-ids", station.ids) + putFloat("$bookmarkKey-posX", station.posX.toFloat()) + putFloat("$bookmarkKey-posY", station.posY.toFloat()) + putMutableType( + this, + "$bookmarkKey-stationId", + station.stationId + ) putString("$bookmarkKey-displayId", displayId) putStringSet("bookmark-list", newBookmark) commit() @@ -329,8 +227,8 @@ class ComposeApp(private val activity: MainActivity) { scope.launch { try { busList = getRoute(Dispatchers.Default, lastStation) + } catch (_: SocketTimeoutException) { } - catch (_: SocketTimeoutException) {} } } } @@ -366,15 +264,15 @@ class ComposeApp(private val activity: MainActivity) { } values is Int -> { editor.putString("$key-type", "int") - editor.putInt("$key-value",values) + editor.putInt("$key-value", values) } values is String -> { editor.putString("$key-type", "string") - editor.putString("$key-value",values) + editor.putString("$key-value", values) } values is Float -> { editor.putString("$key-type", "float") - editor.putFloat("$key-value",values) + editor.putFloat("$key-value", values) } } } @@ -385,33 +283,186 @@ class ComposeApp(private val activity: MainActivity) { } - private suspend fun getStation(dispatcher: CoroutineDispatcher, query: String, cityCode: Int) = withContext(dispatcher) { - activity.client!!.getStation( - name = query, - cityCode = cityCode - ).await() - } + private suspend fun getStation(dispatcher: CoroutineDispatcher, query: String, cityCode: Int) = + withContext(dispatcher) { + activity.client!!.getStation( + name = query, + cityCode = cityCode + ).await() + } - private suspend fun getStationAround(dispatcher: CoroutineDispatcher, posX: Double, posY: Double) = withContext(dispatcher) { + private suspend fun getStationAround( + dispatcher: CoroutineDispatcher, + posX: Double, + posY: Double + ) = withContext(dispatcher) { activity.client!!.getStationAround( posX = posX, posY = posY ).await() } - private suspend fun getRoute(dispatcher: CoroutineDispatcher, lastStation: StationInfo?) = withContext(dispatcher) { - activity.client!!.getRoute( - cityCode = lastStation!!.type, - id = lastStation.routeId - ).await() + private suspend fun getRoute(dispatcher: CoroutineDispatcher, lastStation: StationInfo?) = + withContext(dispatcher) { + activity.client!!.getRoute( + cityCode = lastStation!!.type, + id = lastStation.routeId + ).await() + } + + + private suspend fun getRoute(dispatcher: CoroutineDispatcher, id: String, cityCode: Int) = + withContext(dispatcher) { + activity.client!!.getRoute( + cityCode = cityCode, + id = id + ).await() + } + + @Composable + fun StationSearch(navigationController: NavController, response: (Int) -> Unit) = StationSearch( + activity.getString(R.string.station_search_title), + activity.getString(R.string.station_search_description), + items = listOf( + DropdownQuery(activity.getString(R.string.item_metropolitan), 1), + DropdownQuery(activity.getString(R.string.item_buc), 3) + ), + response + ) + + + @Composable + fun StationGPS(navigationController: NavController) = StationGPS( + activity.getString(R.string.station_gps_title), + activity.getString(R.string.station_gps_description) + ) { + navigationController.navigate( + Screen.StationList.route + "?$STATION_TYPE=${StationListType.GPS_LOCATION_SEARCH}", + ) } - private suspend fun getRoute(dispatcher: CoroutineDispatcher, id: String, cityCode: Int) = withContext(dispatcher) { - activity.client!!.getRoute( - cityCode = cityCode, - id = id - ).await() + @Composable + fun StationStar(navigationController: NavController) = StationStar( + activity.getString(R.string.station_star_title), + activity.getString(R.string.station_star_description) + ) { + navigationController.navigate( + Screen.StationList.route + "?$STATION_TYPE=${StationListType.BOOKMARK}", + ) + } + + + @Composable + fun StationListPage( + stationType: StationListType, + query: String, + cityCode: Int, + scope: CoroutineScope = rememberCoroutineScope(), + onFailed: (CharSequence) -> Unit, + onSuccess: (StationInfo) -> Unit + ) { + var stationList by remember { mutableStateOf>(emptyList()) } + var location by remember { mutableStateOf(null) } + + val permissionResult = rememberMultiplePermissionsState( + listOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + ) + LaunchedEffect(true) { + if (activity.fusedLocationClient != null) { + if (ActivityCompat.checkSelfPermission( + activity, Manifest.permission.ACCESS_FINE_LOCATION + ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission( + activity, Manifest.permission.ACCESS_COARSE_LOCATION + ) != PackageManager.PERMISSION_GRANTED + ) { + permissionResult.launchMultiplePermissionRequest() + if (!permissionResult.allPermissionsGranted) { + onFailed(activity.getText(R.string.gps_permission)) + return@LaunchedEffect + } + } + location = getLocation( + activity.fusedLocationClient!!, + stationType == StationListType.GPS_LOCATION_SEARCH + ) + } + // Log.i("location", "$location") + if (location == null && stationType == StationListType.GPS_LOCATION_SEARCH) { + onFailed(activity.getText(R.string.gps_not_found)) + return@LaunchedEffect + } + try { + stationList = this@ComposeApp.getStationList( + stationType = stationType, + query = query, + cityCode = cityCode, + location = location!! + ) + } catch (e: SocketTimeoutException) { + onFailed(activity.getText(R.string.timeout)) + return@LaunchedEffect + } + } + if (activity.fusedLocationClient == null && stationType == StationListType.GPS_LOCATION_SEARCH) { + onFailed(activity.getText(R.string.gps_not_found)) + return + } + + val title = when (stationType) { + StationListType.SEARCH -> activity.getString(R.string.title_search, query) + StationListType.GPS_LOCATION_SEARCH -> activity.getString(R.string.title_gps_location) + StationListType.BOOKMARK -> activity.getString(R.string.title_bookmark) + else -> activity.getString(R.string.title_search) + } + StationListPage(title, stationList, location, scope, onSuccess) + } + + private suspend fun getStationList( + stationType: StationListType, + query: String, + cityCode: Int, + location: Location? = null + ) = when (stationType) { + StationListType.SEARCH -> { + this@ComposeApp.getStation(defaultDispatcher, query, cityCode) + } + StationListType.GPS_LOCATION_SEARCH -> { + // location != null + val convertData = mutableListOf() + val stationAroundList = + getStationAround(defaultDispatcher, location!!.longitude, location.latitude) + for (st in stationAroundList) { + convertData.add( + st.convertToStationInfo() + ) + } + convertData + } + StationListType.BOOKMARK -> { + val bookmarkStation = mutableListOf() + val sharedPreferences = activity.getPreferences("bookmark") + val bookmark = sharedPreferences.getStringSet("bookmark-list", mutableSetOf()) + bookmark?.forEach { stationId: String -> + // Log.i("Bookmark", stationId) + bookmarkStation.add( + StationInfo( + sharedPreferences.getString("$stationId-name", "알 수 없음") ?: "알 수 없음", + sharedPreferences.getString("$stationId-id", null) ?: "-2", + sharedPreferences.getString("$stationId-ids", null), + sharedPreferences.getFloat("$stationId-posX", 0.0F).toDouble(), + sharedPreferences.getFloat("$stationId-posY", 0.0F).toDouble(), + sharedPreferences.getString("$stationId-displayId", null) ?: "0", + getMutableType(sharedPreferences, "$stationId-stationId", null) ?: "0", + sharedPreferences.getInt("$stationId-type", 0), + ) + ) + } + bookmarkStation + } } } \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 13c4e47..df8b55f 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -42,4 +42,7 @@ End of Service Wait for Departure Unknown + + 버스정류장 곧 도착 버스 정보 + 곧 도착하는 버스 목록을 불러옵니다. \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c5ead15..d2af7e5 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -43,4 +43,7 @@ 운행 종료 출발 대기 정보 없음 + + 곧 도착 버스 + 곧 도착하는 버스 목록을 불러옵니다. \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2da976e..17c65e5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ accompanist = "0.24.6-alpha" retrofit = "2.9.0" compose = "1.3.0-rc01" wearCompose = "1.1.0-alpha07" +wearTile = "1.1.0" [libraries] coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutine" } @@ -53,7 +54,12 @@ wearable = "com.google.android.wearable:wearable:2.9.0" wear = "androidx.wear:wear:1.2.0" wear-input = "androidx.wear:wear-input:1.2.0-alpha02" +wear-tile = { module = "androidx.wear.tiles:tiles", version.ref = "wearTile" } +wear-tile-material = { module = "androidx.wear.tiles:tiles-material", version.ref = "wearTile" } +wear-tile-renderer = { module = "androidx.wear.tiles:tiles-renderer", version.ref = "wearTile" } +wear-tile-testing = { module = "androidx.wear.tiles:tiles-testing", version.ref = "wearTile" } + [bundles] -tests = ["test-junit"] +tests = ["test-junit", "wear-tile-testing"] androidTest = ["test-ext", "compose-ui-test", "espresso"] -debugTest = ["compose-uiTooling"] \ No newline at end of file +debugTest = ["compose-uiTooling", "wear-tile-renderer"] \ No newline at end of file From 177ff41e05e39cbe8983a28829342619be606b94 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Sun, 27 Nov 2022 01:03:01 +0900 Subject: [PATCH 03/23] Base Tile Services --- .../traffic/tiles/ArrivingSoonTileService.kt | 20 ++++----- .../kr/yhs/traffic/tiles/BaseTileService.kt | 44 +++++++++++++++++++ 2 files changed, 52 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/kr/yhs/traffic/tiles/BaseTileService.kt diff --git a/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt index b13ae99..5171171 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt @@ -1,17 +1,13 @@ package kr.yhs.traffic.tiles -import androidx.wear.tiles.RequestBuilders -import androidx.wear.tiles.ResourceBuilders -import androidx.wear.tiles.TileBuilders -import androidx.wear.tiles.TileService +import androidx.wear.tiles.* +import androidx.wear.tiles.DimensionBuilders.expand import com.google.common.util.concurrent.ListenableFuture +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.guava.future -class ArrivingSoonTileService: TileService() { - override fun onTileRequest(requestParams: RequestBuilders.TileRequest): ListenableFuture { - TODO("Not yet implemented") - } - - override fun onResourcesRequest(requestParams: RequestBuilders.ResourcesRequest): ListenableFuture { - TODO("Not yet implemented") - } +class ArrivingSoonTileService : BaseTileService() { + override val RESOURCES_VERSION = "1" } \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/tiles/BaseTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/BaseTileService.kt new file mode 100644 index 0000000..4a80aa2 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/tiles/BaseTileService.kt @@ -0,0 +1,44 @@ +package kr.yhs.traffic.tiles + +import androidx.wear.tiles.* +import androidx.wear.tiles.DimensionBuilders.expand +import com.google.common.util.concurrent.ListenableFuture +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancel +import kotlinx.coroutines.guava.future + +open class BaseTileService : TileService() { + open val RESOURCES_VERSION = "1" + val serviceScope = CoroutineScope(Dispatchers.IO) + lateinit var deviceParameters: DeviceParametersBuilders.DeviceParameters + + override fun onTileRequest(requestParams: RequestBuilders.TileRequest): ListenableFuture = + serviceScope.future { + TileBuilders.Tile.Builder().apply { + setResourcesVersion(RESOURCES_VERSION) + setFreshnessIntervalMillis(3600000) + }.build() + } + + override fun onResourcesRequest(requestParams: RequestBuilders.ResourcesRequest): ListenableFuture = + serviceScope.future { + ResourceBuilders.Resources.Builder().apply { + setVersion(RESOURCES_VERSION) + }.build() + } + + override fun onDestroy() { + super.onDestroy() + serviceScope.cancel() + } + + private fun getLayout(contents: List) = + LayoutElementBuilders.Box.Builder().apply { + setWidth(expand()) + setHeight(expand()) + contents.forEach { + addContent (it) + } + }.build() +} \ No newline at end of file From c7c91204000545176a090ac097e6ee187b384bd2 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Wed, 30 Nov 2022 02:05:16 +0900 Subject: [PATCH 04/23] Wearable Tile --- .../traffic/tiles/ArrivingSoonTileService.kt | 70 +++++++++++++++--- .../kr/yhs/traffic/tiles/BaseTileService.kt | 44 ------------ .../traffic/tiles/CoroutinesTileService.kt | 71 +++++++++++++++++++ 3 files changed, 133 insertions(+), 52 deletions(-) delete mode 100644 app/src/main/java/kr/yhs/traffic/tiles/BaseTileService.kt create mode 100644 app/src/main/java/kr/yhs/traffic/tiles/CoroutinesTileService.kt diff --git a/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt index 5171171..3b6a0c7 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/ArrivingSoonTileService.kt @@ -1,13 +1,67 @@ package kr.yhs.traffic.tiles +import android.content.SharedPreferences +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.edit import androidx.wear.tiles.* import androidx.wear.tiles.DimensionBuilders.expand -import com.google.common.util.concurrent.ListenableFuture -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.cancel -import kotlinx.coroutines.guava.future - -class ArrivingSoonTileService : BaseTileService() { - override val RESOURCES_VERSION = "1" +import androidx.wear.tiles.LayoutElementBuilders.Column +import androidx.wear.tiles.TimelineBuilders.TimelineEntry + +class ArrivingSoonTileService : CoroutinesTileService() { + private val RESOURCES_VERSION = "1" + lateinit var preferences: SharedPreferences + + override fun onCreate() { + super.onCreate() + preferences = getPreferences("ArrivingSoonTile") + } + + override suspend fun tileRequest(requestParams: RequestBuilders.TileRequest): TileBuilders.Tile = + TileBuilders.Tile.Builder().apply { + setResourcesVersion(RESOURCES_VERSION) + setTimeline( + TimelineBuilders.Timeline.Builder() + .addTimelineEntry( + TimelineEntry.Builder() + .setLayout( + LayoutElementBuilders.Layout.Builder() + .setRoot(this@ArrivingSoonTileService.tileLayout(requestParams.deviceParameters!!)) + .build() + ) + .build() + ) + .build() + ) + }.build() + + override suspend fun resourcesRequest(requestParams: RequestBuilders.ResourcesRequest): ResourceBuilders.Resources = + ResourceBuilders.Resources.Builder().apply { + setVersion(RESOURCES_VERSION) + }.build() + + private fun tileLayout(deviceParameters: DeviceParametersBuilders.DeviceParameters) = + Column.Builder().apply { + if (preferences.contains("station")) { + // 등록해! + // addContent() + } else { + // 불러와! + // addContent () + } + setModifiers( + ModifiersBuilders.Modifiers.Builder().apply { + setHeight(expand()) + setWidth(expand()) + }.build() + ) + }.build() + + override fun onDestroy() { + super.onDestroy() + preferences.edit { + remove("station") + commit() + } + } } \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/tiles/BaseTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/BaseTileService.kt deleted file mode 100644 index 4a80aa2..0000000 --- a/app/src/main/java/kr/yhs/traffic/tiles/BaseTileService.kt +++ /dev/null @@ -1,44 +0,0 @@ -package kr.yhs.traffic.tiles - -import androidx.wear.tiles.* -import androidx.wear.tiles.DimensionBuilders.expand -import com.google.common.util.concurrent.ListenableFuture -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.cancel -import kotlinx.coroutines.guava.future - -open class BaseTileService : TileService() { - open val RESOURCES_VERSION = "1" - val serviceScope = CoroutineScope(Dispatchers.IO) - lateinit var deviceParameters: DeviceParametersBuilders.DeviceParameters - - override fun onTileRequest(requestParams: RequestBuilders.TileRequest): ListenableFuture = - serviceScope.future { - TileBuilders.Tile.Builder().apply { - setResourcesVersion(RESOURCES_VERSION) - setFreshnessIntervalMillis(3600000) - }.build() - } - - override fun onResourcesRequest(requestParams: RequestBuilders.ResourcesRequest): ListenableFuture = - serviceScope.future { - ResourceBuilders.Resources.Builder().apply { - setVersion(RESOURCES_VERSION) - }.build() - } - - override fun onDestroy() { - super.onDestroy() - serviceScope.cancel() - } - - private fun getLayout(contents: List) = - LayoutElementBuilders.Box.Builder().apply { - setWidth(expand()) - setHeight(expand()) - contents.forEach { - addContent (it) - } - }.build() -} \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/tiles/CoroutinesTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/CoroutinesTileService.kt new file mode 100644 index 0000000..2538333 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/tiles/CoroutinesTileService.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package kr.yhs.traffic.tiles + +import android.content.SharedPreferences +import androidx.security.crypto.EncryptedSharedPreferences +import androidx.security.crypto.MasterKey +import androidx.wear.tiles.RequestBuilders.ResourcesRequest +import androidx.wear.tiles.RequestBuilders.TileRequest +import androidx.wear.tiles.ResourceBuilders.Resources +import androidx.wear.tiles.TileBuilders.Tile +import androidx.wear.tiles.TileService +import com.google.common.util.concurrent.ListenableFuture +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.guava.future + + +abstract class CoroutinesTileService : TileService() { + private val serviceJob = Job() + private val serviceScope = CoroutineScope(Dispatchers.Main + serviceJob) + lateinit var masterKey: MasterKey + + fun getPreferences(filename: String): SharedPreferences = + EncryptedSharedPreferences.create( + this, filename, masterKey, + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM + ) + + override fun onCreate() { + super.onCreate() + masterKey = MasterKey.Builder(this.baseContext) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build() + } + + final override fun onTileRequest( + requestParams: TileRequest + ): ListenableFuture = serviceScope.future { + tileRequest(requestParams) + } + + abstract suspend fun tileRequest(requestParams: TileRequest): Tile + + final override fun onResourcesRequest(requestParams: ResourcesRequest): + ListenableFuture = serviceScope.future { + resourcesRequest(requestParams) + } + + abstract suspend fun resourcesRequest(requestParams: ResourcesRequest): Resources + + override fun onDestroy() { + super.onDestroy() + serviceJob.cancel() + } +} From 18273002b5046034c4bc56d2f44d6b122eed84f1 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Wed, 30 Nov 2022 12:28:29 +0900 Subject: [PATCH 05/23] wear tiles --- app/build.gradle | 3 + app/src/main/AndroidManifest.xml | 4 +- .../traffic/tiles/ArrivingSoonTileService.kt | 32 +++++---- .../main/java/kr/yhs/traffic/tiles/ImageId.kt | 10 +++ .../yhs/traffic/tiles/settingRequirement.kt | 65 +++++++++++++++++++ .../main/java/kr/yhs/traffic/ui/ComposeApp.kt | 3 +- gradle/libs.versions.toml | 2 + 7 files changed, 102 insertions(+), 17 deletions(-) create mode 100644 app/src/main/java/kr/yhs/traffic/tiles/ImageId.kt create mode 100644 app/src/main/java/kr/yhs/traffic/tiles/settingRequirement.kt diff --git a/app/build.gradle b/app/build.gradle index f4eddb9..c15c2ee 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -77,6 +77,9 @@ dependencies { implementation(libs.compose.wear.foundation) implementation(libs.compose.wear.navigation) + // Horologist + implementation(libs.horologist.tiles) + // Retrofit implementation(libs.retrofit.core) implementation(libs.retrofit.converter.gson) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4cae114..b14e085 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,7 +49,7 @@ - + + \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/SettingTileActivity.kt b/app/src/main/java/kr/yhs/traffic/SettingTileActivity.kt index d7e3935..4b800a2 100644 --- a/app/src/main/java/kr/yhs/traffic/SettingTileActivity.kt +++ b/app/src/main/java/kr/yhs/traffic/SettingTileActivity.kt @@ -6,8 +6,11 @@ import androidx.activity.compose.setContent import androidx.fragment.app.FragmentActivity import androidx.wear.tiles.TileService import kr.yhs.traffic.ui.ComposeSettingTile +import kr.yhs.traffic.utils.ClientBuilder +import kr.yhs.traffic.utils.TrafficClient class SettingTileActivity: FragmentActivity() { + var client: TrafficClient? = null private val sharedPreference = BaseEncryptedSharedPreference(this) fun getPreferences(filename: String) = sharedPreference.getPreferences(filename) @@ -20,6 +23,13 @@ class SettingTileActivity: FragmentActivity() { val tileType = TileType::class.sealedSubclasses.filter { it.objectInstance?.id == clickableId }[0].objectInstance + + val clientBuilder = ClientBuilder() + clientBuilder.httpClient = clientBuilder.httpClientBuild() + + val retrofit = clientBuilder.build() + client = retrofit.create(TrafficClient::class.java) + setContent { ComposeSettingTile(this, tileType!!).Content() } diff --git a/app/src/main/java/kr/yhs/traffic/TileType.kt b/app/src/main/java/kr/yhs/traffic/TileType.kt index f6e618a..5434a2e 100644 --- a/app/src/main/java/kr/yhs/traffic/TileType.kt +++ b/app/src/main/java/kr/yhs/traffic/TileType.kt @@ -1,15 +1,18 @@ package kr.yhs.traffic import androidx.wear.tiles.TileService +import kr.yhs.traffic.tiles.services.Station1TileService import kr.yhs.traffic.tiles.services.Station2TileService sealed class TileType( val title: String, val preferenceId: String, - val classJava: Class + val classJava: Class, + val maxBusSelect: Int ) { - object Station2: TileType("도착 예정 버스", "Station2Tile", Station2TileService::class.java) + object Station1: TileType("실시간 버스 정보(1개)", "Station1Tile", Station1TileService::class.java, 1) + object Station2: TileType("실시간 버스 정보(2개)", "Station2Tile", Station2TileService::class.java, 2) val id: String get() = this.classJava.name diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt new file mode 100644 index 0000000..5dce709 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt @@ -0,0 +1,107 @@ +package kr.yhs.traffic.tiles.services + +import android.content.SharedPreferences +import androidx.core.content.edit +import androidx.wear.tiles.* +import androidx.wear.tiles.DimensionBuilders.expand +import androidx.wear.tiles.TimelineBuilders.TimelineEntry +import com.google.android.horologist.tiles.images.drawableResToImageResource +import kr.yhs.traffic.models.StationInfo +import kr.yhs.traffic.tiles.CoroutinesTileService +import kr.yhs.traffic.tiles.ImageId +import kr.yhs.traffic.tiles.components.SettingRequirement +import kr.yhs.traffic.tiles.components.clickable +import kr.yhs.traffic.utils.ClientBuilder +import kr.yhs.traffic.utils.MutableTypeSharedPreferences +import kr.yhs.traffic.utils.TrafficClient + +abstract class BaseStationTileService( + private val preferencesId: String, + private val resourcesVersion: String +) : CoroutinesTileService(), MutableTypeSharedPreferences { + private lateinit var preferences: SharedPreferences + private var client: TrafficClient? = null + + override fun onCreate() { + super.onCreate() + preferences = getPreferences(preferencesId) + val clientBuilder = ClientBuilder() + clientBuilder.httpClient = clientBuilder.httpClientBuild() + + val retrofit = clientBuilder.build() + client = retrofit.create(TrafficClient::class.java) + } + + override suspend fun tileRequest(requestParams: RequestBuilders.TileRequest): TileBuilders.Tile = + TileBuilders.Tile.Builder().apply { + setResourcesVersion(resourcesVersion) + setFreshnessIntervalMillis(5000) + setTimeline( + TimelineBuilders.Timeline.Builder() + .addTimelineEntry( + TimelineEntry.Builder() + .setLayout( + LayoutElementBuilders.Layout.Builder() + .setRoot(this@BaseStationTileService.tileLayout(requestParams.deviceParameters!!)) + .build() + ) + .build() + ) + .build() + ) + }.build() + + override suspend fun resourcesRequest(requestParams: RequestBuilders.ResourcesRequest): ResourceBuilders.Resources = + ResourceBuilders.Resources.Builder().apply { + setVersion(resourcesVersion) + addIdToImageMapping( + ImageId.Logo.id, + drawableResToImageResource(kr.yhs.traffic.R.mipmap.ic_launcher) + ) + }.build() + + private suspend fun tileLayout(deviceParameters: DeviceParametersBuilders.DeviceParameters): LayoutElementBuilders.LayoutElement { + return LayoutElementBuilders.Box.Builder().apply { + setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER) + setWidth(expand()) + setHeight(expand()) + if (!preferences.contains("station")) { + addContent ( + SettingRequirement(this@BaseStationTileService.baseContext).content( + "도착 예정 버스", "도착할 버스 정보를 불러오기 위한 버스 정류장을 등록해주세요.", + clickable(this@BaseStationTileService) + ) + ) + } else { + val stationInfo = getStationInfo() + addContent( + stationTileLayout(deviceParameters, stationInfo) + ) + } + }.build() + } + + abstract suspend fun stationTileLayout( + deviceParameters: DeviceParametersBuilders.DeviceParameters, + stationInfo: StationInfo + ): LayoutElementBuilders.LayoutElement + + override fun onTileRemoveEvent(requestParams: EventBuilders.TileRemoveEvent) { + super.onTileRemoveEvent(requestParams) + preferences.edit { + remove("station") + commit() + } + } + + private fun getStationInfo() = StationInfo( + this.preferences.getString("station-name", "알 수 없음") ?: "알 수 없음", + this.preferences.getString("station-id", null) ?: "-2", + this.preferences.getString("station-ids", null), + this.preferences.getFloat("station-posX", 0.0F).toDouble(), + this.preferences.getFloat("station-posY", 0.0F).toDouble(), + this.preferences.getString("station-displayId", null) ?: "0", + getMutableType(this.preferences, "station-stationId", null) ?: "0", + this.preferences.getInt("station-type", 0), + ) +} diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt new file mode 100644 index 0000000..853ecee --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt @@ -0,0 +1,13 @@ +package kr.yhs.traffic.tiles.services + +import androidx.wear.tiles.* +import kr.yhs.traffic.models.StationInfo +import kr.yhs.traffic.tiles.components.titleText + +class Station1TileService : BaseStationTileService("Station1Tile", "1") { + override suspend fun stationTileLayout(deviceParameters: DeviceParametersBuilders.DeviceParameters, stationInfo: StationInfo): LayoutElementBuilders.LayoutElement + = LayoutElementBuilders.Column.Builder() + .apply { + addContent(titleText(stationInfo)) + }.build() +} diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt index 2746406..419a26c 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt @@ -1,102 +1,13 @@ package kr.yhs.traffic.tiles.services -import android.content.SharedPreferences -import androidx.core.content.edit import androidx.wear.tiles.* -import androidx.wear.tiles.DimensionBuilders.expand -import androidx.wear.tiles.TimelineBuilders.TimelineEntry -import com.google.android.horologist.tiles.images.drawableResToImageResource import kr.yhs.traffic.models.StationInfo -import kr.yhs.traffic.tiles.CoroutinesTileService -import kr.yhs.traffic.tiles.ImageId -import kr.yhs.traffic.tiles.components.SettingRequirement -import kr.yhs.traffic.tiles.components.clickable import kr.yhs.traffic.tiles.components.titleText -import kr.yhs.traffic.utils.ClientBuilder -import kr.yhs.traffic.utils.MutableTypeSharedPreferences -import kr.yhs.traffic.utils.TrafficClient -class Station2TileService : CoroutinesTileService(), MutableTypeSharedPreferences { - private val RESOURCES_VERSION = "1" - private lateinit var preferences: SharedPreferences - private var client: TrafficClient? = null - - override fun onCreate() { - super.onCreate() - preferences = getPreferences("Station2Tile") - } - - override suspend fun tileRequest(requestParams: RequestBuilders.TileRequest): TileBuilders.Tile = - TileBuilders.Tile.Builder().apply { - setResourcesVersion(RESOURCES_VERSION) - setFreshnessIntervalMillis(5000) - setTimeline( - TimelineBuilders.Timeline.Builder() - .addTimelineEntry( - TimelineEntry.Builder() - .setLayout( - LayoutElementBuilders.Layout.Builder() - .setRoot(this@Station2TileService.tileLayout(requestParams.deviceParameters!!)) - .build() - ) - .build() - ) - .build() - ) - }.build() - - override suspend fun resourcesRequest(requestParams: RequestBuilders.ResourcesRequest): ResourceBuilders.Resources = - ResourceBuilders.Resources.Builder().apply { - setVersion(RESOURCES_VERSION) - addIdToImageMapping( - ImageId.Logo.id, - drawableResToImageResource(kr.yhs.traffic.R.mipmap.ic_launcher) - ) - }.build() - - private suspend fun tileLayout(deviceParameters: DeviceParametersBuilders.DeviceParameters): LayoutElementBuilders.LayoutElement { - return LayoutElementBuilders.Box.Builder().apply { - setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER) - setWidth(expand()) - setHeight(expand()) - if (!preferences.contains("station")) { - addContent ( - SettingRequirement(this@Station2TileService.baseContext).content( - "도착 예정 버스", "곧 도착할 버스 정보를 불러오기 위한 버스 정류장을 등록해주세요.", - clickable(this@Station2TileService) - ) - ) - } else { - val clientBuilder = ClientBuilder() - clientBuilder.httpClient = clientBuilder.httpClientBuild() - - val retrofit = clientBuilder.build() - client = retrofit.create(TrafficClient::class.java) - - val station = this@Station2TileService.getStationInfo() - addContent( - titleText(station) - ) - } - }.build() - } - - override fun onTileRemoveEvent(requestParams: EventBuilders.TileRemoveEvent) { - super.onTileRemoveEvent(requestParams) - preferences.edit { - remove("station") - commit() - } - } - - private fun getStationInfo() = StationInfo( - this.preferences.getString("station-name", "알 수 없음") ?: "알 수 없음", - this.preferences.getString("station-id", null) ?: "-2", - this.preferences.getString("station-ids", null), - this.preferences.getFloat("station-posX", 0.0F).toDouble(), - this.preferences.getFloat("station-posY", 0.0F).toDouble(), - this.preferences.getString("station-displayId", null) ?: "0", - getMutableType(this.preferences, "station-stationId", null) ?: "0", - this.preferences.getInt("station-type", 0), - ) +class Station2TileService : BaseStationTileService("Station2Tile", "1") { + override suspend fun stationTileLayout(deviceParameters: DeviceParametersBuilders.DeviceParameters, stationInfo: StationInfo): LayoutElementBuilders.LayoutElement + = LayoutElementBuilders.Column.Builder() + .apply { + addContent(titleText(stationInfo)) + }.build() } diff --git a/app/src/main/java/kr/yhs/traffic/ui/BaseCompose.kt b/app/src/main/java/kr/yhs/traffic/ui/BaseCompose.kt index 0d293d9..6258abf 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/BaseCompose.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/BaseCompose.kt @@ -2,11 +2,15 @@ package kr.yhs.traffic.ui import android.content.SharedPreferences import androidx.compose.runtime.Composable +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.utils.MutableTypeSharedPreferences +import kr.yhs.traffic.utils.TrafficClient +import retrofit2.await -abstract class BaseCompose: MutableTypeSharedPreferences { +abstract class BaseCompose(private val client: TrafficClient?): MutableTypeSharedPreferences { @Composable abstract fun Content() @@ -32,4 +36,42 @@ abstract class BaseCompose: MutableTypeSharedPreferences { } return bookmarkStation } + + + suspend fun getStation(dispatcher: CoroutineDispatcher, query: String, cityCode: Int) = + withContext(dispatcher) { + client!!.getStation( + name = query, + cityCode = cityCode + ).await() + } + + + suspend fun getStationAround( + dispatcher: CoroutineDispatcher, + posX: Double, + posY: Double + ) = withContext(dispatcher) { + client!!.getStationAround( + posX = posX, posY = posY + ).await() + } + + + suspend fun getRoute(dispatcher: CoroutineDispatcher, lastStation: StationInfo?) = + withContext(dispatcher) { + client!!.getRoute( + cityCode = lastStation!!.type, + id = lastStation.routeId + ).await() + } + + + suspend fun getRoute(dispatcher: CoroutineDispatcher, id: String, cityCode: Int) = + withContext(dispatcher) { + client!!.getRoute( + cityCode = cityCode, + id = id + ).await() + } } \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt index 8f4fd51..cd636aa 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt @@ -43,7 +43,7 @@ import retrofit2.await import java.net.SocketTimeoutException -class ComposeApp(private val activity: MainActivity): BaseCompose() { +class ComposeApp(private val activity: MainActivity): BaseCompose(activity.client) { private val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO override fun getPreferences(filename: String): SharedPreferences = activity.getPreferences(filename) @@ -130,7 +130,6 @@ class ComposeApp(private val activity: MainActivity): BaseCompose() { ) } ) - } composable( Screen.StationInfo.route @@ -239,44 +238,6 @@ class ComposeApp(private val activity: MainActivity): BaseCompose() { } } - - private suspend fun getStation(dispatcher: CoroutineDispatcher, query: String, cityCode: Int) = - withContext(dispatcher) { - activity.client!!.getStation( - name = query, - cityCode = cityCode - ).await() - } - - - private suspend fun getStationAround( - dispatcher: CoroutineDispatcher, - posX: Double, - posY: Double - ) = withContext(dispatcher) { - activity.client!!.getStationAround( - posX = posX, posY = posY - ).await() - } - - - private suspend fun getRoute(dispatcher: CoroutineDispatcher, lastStation: StationInfo?) = - withContext(dispatcher) { - activity.client!!.getRoute( - cityCode = lastStation!!.type, - id = lastStation.routeId - ).await() - } - - - private suspend fun getRoute(dispatcher: CoroutineDispatcher, id: String, cityCode: Int) = - withContext(dispatcher) { - activity.client!!.getRoute( - cityCode = cityCode, - id = id - ).await() - } - @Composable fun StationSearch(response: (Int) -> Unit) = StationSearch( activity.getString(R.string.station_search_title), diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt index 068ba40..0ae6bd5 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt @@ -1,69 +1,147 @@ package kr.yhs.traffic.ui import android.content.SharedPreferences +import android.util.Log import androidx.compose.runtime.* import androidx.core.content.edit import androidx.wear.tiles.TileService +import androidx.wear.widget.ConfirmationOverlay import com.google.accompanist.pager.rememberPagerState +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kr.yhs.traffic.R import kr.yhs.traffic.SettingTileActivity import kr.yhs.traffic.TileType import kr.yhs.traffic.models.StationInfo -import kr.yhs.traffic.ui.components.StepPage +import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.ui.components.AccompanistPager +import kr.yhs.traffic.ui.components.StepPage +import kr.yhs.traffic.ui.pages.RouteSelection import kr.yhs.traffic.ui.pages.StationListPage -import kotlin.system.exitProcess +import retrofit2.HttpException +import java.net.SocketTimeoutException + +class ComposeSettingTile( + private val activity: SettingTileActivity, + private val tileType: TileType +) : BaseCompose(activity.client) { + private val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO -class ComposeSettingTile(private val activity: SettingTileActivity, private val tileType: TileType): BaseCompose() { - override fun getPreferences(filename: String): SharedPreferences = activity.getPreferences(filename) + override fun getPreferences(filename: String): SharedPreferences = + activity.getPreferences(filename) @Composable override fun Content() { val coroutineScope = rememberCoroutineScope() val pagerState = rememberPagerState() var station by remember { mutableStateOf(null) } + var route by remember { mutableStateOf>(listOf()) } + val preferences = getPreferences(tileType.preferenceId) AccompanistPager( scope = coroutineScope, pagerState = pagerState, pages = listOf({ - StepPage(title = tileType.title, description = "${tileType.title}에 불러올 등록할 정류장를 선택해주세요. 즐겨찾기에 등록되어 있어야합니다.") { + StepPage( + title = tileType.title, + description = "${tileType.title}에 불러올 등록할 정류장를 선택해주세요. 즐겨찾기에 등록되어 있어야합니다." + ) { coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) } } }, { val bookmarkStation = getStationBookmarkList() - val preferences = getPreferences(tileType.preferenceId) StationListPage( "등록할 정류장", bookmarkStation, null, coroutineScope, pagerState.currentPage == 1 ) { - val displayId = if (it.displayId is List<*>) { - it.displayId.joinToString(", ") - } else it.displayId?.toString() ?: " " station = it + coroutineScope.launch { + pagerState.animateScrollToPage(pagerState.currentPage + 1) + } + } + }, { + if (activity.client == null) { + ConfirmationOverlay() + .setType(ConfirmationOverlay.FAILURE_ANIMATION) + .setMessage(activity.getText(R.string.station_not_found)) + .showOn(activity) + activity.finish() + return@listOf + } + var routeInfo by remember { + mutableStateOf(listOf()) + } + var isLoaded by remember { mutableStateOf(false) } + Log.i("stationInfo", station.toString()) + if (station != null) { + LaunchedEffect(true) { + try { + routeInfo = getRoute( + defaultDispatcher, + station?.routeId!!, + station?.type!! + ) + isLoaded = true + } catch (e: Exception) { + when (e) { + is SocketTimeoutException, is HttpException -> { + ConfirmationOverlay() + .setType(ConfirmationOverlay.FAILURE_ANIMATION) + .setMessage(activity.getText(R.string.timeout)) + .showOn(activity) + activity.finish() + return@LaunchedEffect + } + else -> throw e + } + } + } + + RouteSelection(this.activity).Content( + station!!, + routeInfo, + coroutineScope, + isLoaded, + tileType.maxBusSelect + ) { + coroutineScope.launch { + pagerState.animateScrollToPage(pagerState.currentPage + 1) + } + preferences.edit { + this.putStringSet("busRoute", it.map { it.id }.toSet()) + } + } + } + }, { + if (station != null && route.isNotEmpty()) { + val privateStation = station!! + val displayId = if (privateStation.displayId is List<*>) { + privateStation.displayId.joinToString(", ") + } else privateStation.displayId?.toString() ?: " " preferences.edit { - putString("station", it.routeId) - putString("station-name", it.name) - putInt("station-type", it.type) - putString("station-id", it.id) - putString("station-ids", it.ids) - putFloat("station-posX", it.posX.toFloat()) - putFloat("station-posY", it.posY.toFloat()) + putString("station", privateStation.routeId) + putString("station-name", privateStation.name) + putInt("station-type", privateStation.type) + putString("station-id", privateStation.id) + putString("station-ids", privateStation.ids) + putFloat("station-posX", privateStation.posX.toFloat()) + putFloat("station-posY", privateStation.posY.toFloat()) putMutableType( this, "station-stationId", - it.stationId + privateStation.stationId ) putString("station-displayId", displayId) commit() } - coroutineScope.launch { - pagerState.animateScrollToPage(pagerState.currentPage + 1) - } } - }, { - StepPage(title = tileType.title, description = "성공적으로 ${tileType.title}에 ${station?.name}(${station?.displayId})을 등록하였습니다.", "완료") { + StepPage( + title = tileType.title, + description = "성공적으로 ${tileType.title}에 ${station?.name}(${station?.displayId})을 등록하였습니다.", + "완료" + ) { TileService.getUpdater(activity.baseContext).requestUpdate(tileType.classJava) - exitProcess(0) + activity.finish() } }), userScrollEnabled = false, diff --git a/app/src/main/java/kr/yhs/traffic/ui/components/LoadingProgressIndicator.kt b/app/src/main/java/kr/yhs/traffic/ui/components/LoadingProgressIndicator.kt new file mode 100644 index 0000000..0fb18f2 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/ui/components/LoadingProgressIndicator.kt @@ -0,0 +1,16 @@ +package kr.yhs.traffic.ui.components + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material.* + + +@Composable +fun LoadingProgressIndicator(modifier: Modifier = Modifier) + = CircularProgressIndicator( + modifier = modifier, + indicatorColor = MaterialTheme.colors.secondary, + trackColor = MaterialTheme.colors.onBackground.copy(alpha = 0.1f), + strokeWidth = 4.dp + ) \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt new file mode 100644 index 0000000..1852d2d --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt @@ -0,0 +1,190 @@ +package kr.yhs.traffic.ui.pages + +import android.app.Activity +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.animateScrollBy +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.rotary.onRotaryScrollEvent +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.wear.compose.material.* +import androidx.wear.widget.ConfirmationOverlay +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import kr.yhs.traffic.models.StationInfo +import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.ui.components.LoadingProgressIndicator +import kr.yhs.traffic.ui.components.NextButton +import kr.yhs.traffic.ui.theme.BusColor + + +class RouteSelection(private val context: Activity) { + @OptIn(ExperimentalComposeUiApi::class) + @Composable + fun Content( + stationInfo: StationInfo, + busInfo: List, + scope: CoroutineScope, + isLoaded: Boolean = false, + maxSelect: Int = 1, + callback: (List) -> Unit + ) { + val scalingLazyListState = rememberScalingLazyListState() + Scaffold( + positionIndicator = { + PositionIndicator(scalingLazyListState = scalingLazyListState) + } + ) { + val checkedRoute = mutableListOf() + var itemIndex by remember { mutableStateOf(1) } + ScalingLazyColumn( + state = scalingLazyListState, + modifier = Modifier + .fillMaxSize() + .onRotaryScrollEvent { + scope.launch { + scalingLazyListState.animateScrollBy(it.horizontalScrollPixels) + } + true + }, + // .focusRequester(focusRequester) + // .focusable(), + contentPadding = PaddingValues(16.dp), + autoCentering = AutoCenteringParams(itemIndex = itemIndex) + ) { + item { + RouteSelectionTitle(maxSelect) + } + if (isLoaded) { + items(busInfo) { + var checked by remember { + mutableStateOf(false) + } + SmallToggleChip(it, checked) { + if (checked) { + checkedRoute.remove(it) + checked = false + } else { + when (checkedRoute.size < maxSelect) { + true -> { + checkedRoute.add(it) + checked = true + } + else -> { + ConfirmationOverlay() + .setType(ConfirmationOverlay.FAILURE_ANIMATION) + .setMessage("최대 ${maxSelect}개까지 등록할 수 있습니다.") + .showOn(context) + } + } + } + } + } + } else { + item { + itemIndex = 2 + LoadingProgressIndicator() + } + } + item { + NextButton( + modifier = Modifier.padding(top = 30.dp) + ) { + if (checkedRoute.size < 1) { + ConfirmationOverlay() + .setType(ConfirmationOverlay.FAILURE_ANIMATION) + .setMessage("최소 1개 이상 선택해 주세요.") + .showOn(context) + return@NextButton + } + callback.invoke(checkedRoute) + } + } + } + } + } + + @Composable + private fun RouteSelectionTitle(maxSelect: Int) = + Column( + modifier = Modifier.padding(bottom = 5.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + Text( + text = "버스 노선 선택", + color = Color.White, + fontSize = 18.sp, + maxLines = 1, + fontWeight = FontWeight.Bold, + textAlign = TextAlign.Center + ) + Text( + text = "버스정류장에 경유하는 노선 중 최대 ${maxSelect}개까지 선택해주세요.", + color = Color.White, + fontSize = 12.sp, + maxLines = 2, + textAlign = TextAlign.Center + ) + Text( + text = "선택 후 다음 버튼을 눌러주세요.", + color = Color.White, + fontSize = 12.sp, + maxLines = 1, + textAlign = TextAlign.Center + ) + } + + @Composable + private fun SmallToggleChip(busInfo: StationRoute, checked: Boolean, onClick: () -> Unit) { + var backgroundColor = BusColor.Default + for (busColor in BusColor.values()) { + if (busInfo.type == busColor.typeCode) { + backgroundColor = busColor + break + } + } + Chip( + modifier = Modifier + .fillMaxWidth() + .height(32.dp), + onClick = onClick, + label = { + Spacer( + modifier = Modifier + .width(20.dp) + .height(20.dp) + .clip(CircleShape) + .background(backgroundColor.color) + ) + Text( + text = busInfo.name, + modifier = Modifier.padding(start = 4.dp) + ) + Spacer( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + ) + Checkbox( + checked = checked, + modifier = Modifier.semantics { + this.contentDescription = + if (checked) "Checked" else "Unchecked" + } + ) + } + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index df8b55f..fbded1d 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -43,6 +43,9 @@ Wait for Departure Unknown - 버스정류장 곧 도착 버스 정보 - 곧 도착하는 버스 목록을 불러옵니다. + 버스정류장 곧 도착 버스 정보 + 곧 도착하는 버스 목록을 불러옵니다. + + 버스정류장 곧 도착 버스 정보 + 곧 도착하는 버스 목록을 불러옵니다. \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d2af7e5..84456bf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -44,6 +44,9 @@ 출발 대기 정보 없음 - 곧 도착 버스 - 곧 도착하는 버스 목록을 불러옵니다. + 버스정류장 실시간 도착정보(1개) + 해당하는 버스 정류장의 도착정보를 불러옵니다. + + 버스정류장 실시간 도착정보(2개) + 해당하는 버스 정류장의 도착정보를 불러옵니다. \ No newline at end of file From 78a45bcd4f5b8a8f000c53e500da4841dbdbf133 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Mon, 12 Dec 2022 01:58:56 +0900 Subject: [PATCH 14/23] Add LoadingProgressIndicator --- .../main/java/kr/yhs/traffic/ui/ComposeApp.kt | 8 ++++-- .../kr/yhs/traffic/ui/ComposeSettingTile.kt | 2 +- .../ui/components/LoadingProgressIndicator.kt | 23 ++++++++++++++--- .../kr/yhs/traffic/ui/pages/RouteSelection.kt | 1 + .../yhs/traffic/ui/pages/StationInfoPage.kt | 15 ++++++++--- .../yhs/traffic/ui/pages/StationListPage.kt | 25 ++++++++++++------- 6 files changed, 56 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt index cd636aa..937c4e1 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt @@ -135,6 +135,7 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien Screen.StationInfo.route ) { var busList by remember { mutableStateOf>(emptyList()) } + var isLoading by remember { mutableStateOf(true) } if (lastStation == null) { ConfirmationOverlay() .setType(ConfirmationOverlay.FAILURE_ANIMATION) @@ -151,6 +152,7 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien station.routeId, station.type ) + isLoading = false } catch (e: Exception) { when (e) { is SocketTimeoutException, is HttpException -> { @@ -173,7 +175,7 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien StationInfoPage( station, busList, - bookmark.contains(bookmarkKey), scope + bookmark.contains(bookmarkKey), isLoading, scope ) { when (it) { StationInfoSelection.BOOKMARK -> { @@ -283,6 +285,7 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien ) { var stationList by remember { mutableStateOf>(emptyList()) } var location by remember { mutableStateOf(null) } + var isLoading by remember { mutableStateOf(true) } val permissionResult = rememberMultiplePermissionsState( listOf( @@ -321,6 +324,7 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien cityCode = cityCode, location = location!! ) + isLoading = false } catch (e: SocketTimeoutException) { onFailed(activity.getText(R.string.timeout)) return@LaunchedEffect @@ -336,7 +340,7 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien StationListType.GPS_LOCATION_SEARCH -> activity.getString(R.string.title_gps_location) StationListType.BOOKMARK -> activity.getString(R.string.title_bookmark) } - StationListPage(title, stationList, location, scope, true, onSuccess) + StationListPage(title, stationList, location, scope, isLoading, true, onSuccess) } private suspend fun getStationList( diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt index 0ae6bd5..3eafeb6 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt @@ -52,7 +52,7 @@ class ComposeSettingTile( }, { val bookmarkStation = getStationBookmarkList() StationListPage( - "등록할 정류장", bookmarkStation, null, coroutineScope, pagerState.currentPage == 1 + "등록할 정류장", bookmarkStation, null, coroutineScope, false, pagerState.currentPage == 1 ) { station = it coroutineScope.launch { diff --git a/app/src/main/java/kr/yhs/traffic/ui/components/LoadingProgressIndicator.kt b/app/src/main/java/kr/yhs/traffic/ui/components/LoadingProgressIndicator.kt index 0fb18f2..fc93b5a 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/components/LoadingProgressIndicator.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/components/LoadingProgressIndicator.kt @@ -1,16 +1,33 @@ package kr.yhs.traffic.ui.components +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.wear.compose.material.* @Composable -fun LoadingProgressIndicator(modifier: Modifier = Modifier) - = CircularProgressIndicator( +fun LoadingProgressIndicator(modifier: Modifier = Modifier) = Row( + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically +) { + CircularProgressIndicator( modifier = modifier, indicatorColor = MaterialTheme.colors.secondary, trackColor = MaterialTheme.colors.onBackground.copy(alpha = 0.1f), strokeWidth = 4.dp - ) \ No newline at end of file + ) + Text( + text = "불러오는 중", + color = Color.LightGray, + fontSize = 14.sp, + modifier = Modifier.padding(start = 6.dp) + ) +} \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt index 1852d2d..5831b98 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt @@ -68,6 +68,7 @@ class RouteSelection(private val context: Activity) { } if (isLoaded) { items(busInfo) { + itemIndex = 1 var checked by remember { mutableStateOf(false) } diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt index f4e4622..ff9ed90 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt @@ -24,6 +24,7 @@ import kotlinx.coroutines.launch import kr.yhs.traffic.R import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.ui.components.LoadingProgressIndicator import kr.yhs.traffic.utils.StopWatch import kr.yhs.traffic.ui.theme.BusColor import kr.yhs.traffic.ui.theme.StationInfoSelection @@ -34,6 +35,7 @@ fun StationInfoPage( stationInfo: StationInfo, busInfo: List, starActive: Boolean = false, + isLoading: Boolean = false, scope: CoroutineScope, callback: (StationInfoSelection) -> Unit ) { @@ -52,7 +54,8 @@ fun StationInfoPage( stopWatch.start() ScalingLazyColumn( state = scalingLazyListState, - modifier = Modifier.fillMaxSize() + modifier = Modifier + .fillMaxSize() .onRotaryScrollEvent { scope.launch { scalingLazyListState.animateScrollBy(it.horizontalScrollPixels) @@ -66,8 +69,14 @@ fun StationInfoPage( item { StationTitle(stationInfo.name) } - items(busInfo) { - StationRoute(it, stopWatch.timeMillis.toInt()) + if (!isLoading) { + items(busInfo) { + StationRoute(it, stopWatch.timeMillis.toInt()) + } + } else { + item { + LoadingProgressIndicator() + } } item { Row( diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/StationListPage.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/StationListPage.kt index eb9a9ec..2191d84 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/StationListPage.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/StationListPage.kt @@ -32,6 +32,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import kr.yhs.traffic.R import kr.yhs.traffic.models.StationInfo +import kr.yhs.traffic.ui.components.LoadingProgressIndicator import kotlin.math.atan2 import kotlin.math.roundToInt @@ -42,6 +43,7 @@ fun StationListPage( stationList: List, location: Location?, coroutineScope: CoroutineScope, + isLoading: Boolean = false, rotaryScrollEnable: Boolean = true, stationCallback: (StationInfo) -> Unit ) { @@ -49,12 +51,13 @@ fun StationListPage( val focusRequester = remember { FocusRequester() } var modifier = Modifier.fillMaxSize() if (rotaryScrollEnable) { - modifier = modifier.onRotaryScrollEvent { - coroutineScope.launch { - scalingLazyListState.animateScrollBy(it.horizontalScrollPixels) + modifier = modifier + .onRotaryScrollEvent { + coroutineScope.launch { + scalingLazyListState.animateScrollBy(it.horizontalScrollPixels) + } + true } - true - } .focusRequester(focusRequester) .focusable() } @@ -103,10 +106,10 @@ fun StationListPage( ) distance = result[0].toInt() direction = ( - atan2( - location.latitude - station.posY, - station.posX - location.longitude - ) * 180 / Math.PI + atan2( + location.latitude - station.posY, + station.posX - location.longitude + ) * 180 / Math.PI ).roundToInt() - location.bearing.roundToInt() } StationShortInfo( @@ -117,6 +120,10 @@ fun StationListPage( stationCallback(station) } } + } else if (isLoading) { + item { + LoadingProgressIndicator() + } } else { item { StationEmpty() From 5acd664f500ad490040f8cf6cbcf634f8c63fdc8 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Mon, 12 Dec 2022 02:16:02 +0900 Subject: [PATCH 15/23] Tile Setting Error Fix --- app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt index 3eafeb6..f6faeb7 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt @@ -107,6 +107,7 @@ class ComposeSettingTile( coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) } + route = it preferences.edit { this.putStringSet("busRoute", it.map { it.id }.toSet()) } From 6ca8689f4007ddaa115dadecce851db4143d6acd Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Tue, 13 Dec 2022 01:56:39 +0900 Subject: [PATCH 16/23] Add Bus Route Text but, deprecated (server issue) --- .../traffic/tiles/components/busRouteText.kt | 49 +++++++++++++++++++ .../{titleText.kt => stationText.kt} | 2 +- .../tiles/services/BaseStationTileService.kt | 19 +++++-- .../tiles/services/Station1TileService.kt | 29 +++++++++-- .../tiles/services/Station2TileService.kt | 11 +++-- .../kr/yhs/traffic/ui/ComposeSettingTile.kt | 7 +-- .../kr/yhs/traffic/ui/pages/RouteSelection.kt | 33 +++++++++---- 7 files changed, 124 insertions(+), 26 deletions(-) create mode 100644 app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt rename app/src/main/java/kr/yhs/traffic/tiles/components/{titleText.kt => stationText.kt} (86%) diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt new file mode 100644 index 0000000..a2087d9 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt @@ -0,0 +1,49 @@ +package kr.yhs.traffic.tiles.components + +import androidx.compose.ui.graphics.toArgb +import androidx.wear.tiles.ColorBuilders.ColorProp +import androidx.wear.tiles.DimensionBuilders.dp +import androidx.wear.tiles.DimensionBuilders.sp +import androidx.wear.tiles.LayoutElementBuilders +import androidx.wear.tiles.LayoutElementBuilders.FontStyle +import androidx.wear.tiles.LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END +import androidx.wear.tiles.ModifiersBuilders +import androidx.wear.tiles.ModifiersBuilders.Background +import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.ui.theme.BusColor + + +fun busRouteText(busInfo: StationRoute) = LayoutElementBuilders.Text.Builder().apply { + var backgroundColor = BusColor.Default + for (busColor in BusColor.values()) { + if (busInfo.type == busColor.typeCode) { + backgroundColor = busColor + break + } + } + + setText(busInfo.name) + setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END) + setFontStyle( + FontStyle.Builder() + .setSize(sp(16f)) + .build() + ) + setMaxLines(1) + setModifiers( + ModifiersBuilders.Modifiers.Builder() + .setBackground( + Background.Builder() + .setColor( + ColorProp.Builder() + .setArgb(backgroundColor.color.toArgb()) + .build() + ).setCorner( + ModifiersBuilders.Corner.Builder() + .setRadius(dp(3f)) + .build() + ).build() + ) + .build() + ) +}.build() diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/titleText.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt similarity index 86% rename from app/src/main/java/kr/yhs/traffic/tiles/components/titleText.kt rename to app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt index e5c8fdb..1f0c3fe 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/components/titleText.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt @@ -7,7 +7,7 @@ import androidx.wear.tiles.LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END import kr.yhs.traffic.models.StationInfo -fun titleText(stationInfo: StationInfo) = LayoutElementBuilders.Text.Builder() +fun stationText(stationInfo: StationInfo) = LayoutElementBuilders.Text.Builder() .setText(stationInfo.name) .setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END) .setFontStyle( diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt index 5dce709..3e67cb5 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt @@ -7,6 +7,7 @@ import androidx.wear.tiles.DimensionBuilders.expand import androidx.wear.tiles.TimelineBuilders.TimelineEntry import com.google.android.horologist.tiles.images.drawableResToImageResource import kr.yhs.traffic.models.StationInfo +import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.tiles.CoroutinesTileService import kr.yhs.traffic.tiles.ImageId import kr.yhs.traffic.tiles.components.SettingRequirement @@ -35,7 +36,7 @@ abstract class BaseStationTileService( override suspend fun tileRequest(requestParams: RequestBuilders.TileRequest): TileBuilders.Tile = TileBuilders.Tile.Builder().apply { setResourcesVersion(resourcesVersion) - setFreshnessIntervalMillis(5000) + setFreshnessIntervalMillis(1000) setTimeline( TimelineBuilders.Timeline.Builder() .addTimelineEntry( @@ -74,16 +75,24 @@ abstract class BaseStationTileService( ) } else { val stationInfo = getStationInfo() - addContent( - stationTileLayout(deviceParameters, stationInfo) - ) + val busRouteId = preferences.getStringSet("busRoute", setOf()) + + val routeInfo = /* withContext(Dispatchers.IO) { + client?.getRoute( + stationInfo.routeId, stationInfo.type + )?.await() + }?.filter { busRouteId?.contains(it.id) == true } */ null + /* addContent( + stationTileLayout(deviceParameters, stationInfo, routeInfo) + ) */ } }.build() } abstract suspend fun stationTileLayout( deviceParameters: DeviceParametersBuilders.DeviceParameters, - stationInfo: StationInfo + stationInfo: StationInfo, + routeInfo: List? ): LayoutElementBuilders.LayoutElement override fun onTileRemoveEvent(requestParams: EventBuilders.TileRemoveEvent) { diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt index 853ecee..95e4f77 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt @@ -1,13 +1,34 @@ package kr.yhs.traffic.tiles.services -import androidx.wear.tiles.* +import android.util.Log +import androidx.wear.tiles.DeviceParametersBuilders +import androidx.wear.tiles.LayoutElementBuilders import kr.yhs.traffic.models.StationInfo -import kr.yhs.traffic.tiles.components.titleText +import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.tiles.components.stationText class Station1TileService : BaseStationTileService("Station1Tile", "1") { - override suspend fun stationTileLayout(deviceParameters: DeviceParametersBuilders.DeviceParameters, stationInfo: StationInfo): LayoutElementBuilders.LayoutElement + override suspend fun stationTileLayout( + deviceParameters: DeviceParametersBuilders.DeviceParameters, + stationInfo: StationInfo, + busRoute: List? + ): LayoutElementBuilders.LayoutElement = LayoutElementBuilders.Column.Builder() .apply { - addContent(titleText(stationInfo)) + // addContent(busRouteText(busRoute!![0])) + addContent(stationText(stationInfo)) + if (busRoute != null) { + Log.i("bus", busRoute.toString()) + addContent( + LayoutElementBuilders.Text.Builder() + .setText(busRoute[0].name) + .build() + ) + addContent( + LayoutElementBuilders.Text.Builder() + .setText(busRoute[0].arrivalInfo[0].time.toString()) + .build() + ) + } }.build() } diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt index 419a26c..549774a 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt @@ -2,12 +2,17 @@ package kr.yhs.traffic.tiles.services import androidx.wear.tiles.* import kr.yhs.traffic.models.StationInfo -import kr.yhs.traffic.tiles.components.titleText +import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.tiles.components.stationText class Station2TileService : BaseStationTileService("Station2Tile", "1") { - override suspend fun stationTileLayout(deviceParameters: DeviceParametersBuilders.DeviceParameters, stationInfo: StationInfo): LayoutElementBuilders.LayoutElement + override suspend fun stationTileLayout( + deviceParameters: DeviceParametersBuilders.DeviceParameters, + stationInfo: StationInfo, + busRoute: List? + ): LayoutElementBuilders.LayoutElement = LayoutElementBuilders.Column.Builder() .apply { - addContent(titleText(stationInfo)) + addContent(stationText(stationInfo)) }.build() } diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt index f6faeb7..c457a2f 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt @@ -102,14 +102,15 @@ class ComposeSettingTile( routeInfo, coroutineScope, isLoaded, + pagerState.currentPage == 2, tileType.maxBusSelect - ) { + ) { stationRoute -> coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) } - route = it + route = stationRoute preferences.edit { - this.putStringSet("busRoute", it.map { it.id }.toSet()) + this.putStringSet("busRoute", stationRoute.map { it.id }.toSet()) } } } diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt index 5831b98..71dbc68 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt @@ -2,6 +2,7 @@ package kr.yhs.traffic.ui.pages import android.app.Activity import androidx.compose.foundation.background +import androidx.compose.foundation.focusable import androidx.compose.foundation.gestures.animateScrollBy import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape @@ -10,6 +11,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.rotary.onRotaryScrollEvent import androidx.compose.ui.semantics.contentDescription @@ -37,10 +40,24 @@ class RouteSelection(private val context: Activity) { busInfo: List, scope: CoroutineScope, isLoaded: Boolean = false, + rotaryScrollEnable: Boolean = true, maxSelect: Int = 1, callback: (List) -> Unit ) { val scalingLazyListState = rememberScalingLazyListState() + val focusRequester = remember { FocusRequester() } + var modifier = Modifier.fillMaxSize() + if (rotaryScrollEnable) { + modifier = modifier + .onRotaryScrollEvent { + scope.launch { + scalingLazyListState.animateScrollBy(it.horizontalScrollPixels) + } + true + } + .focusRequester(focusRequester) + .focusable() + } Scaffold( positionIndicator = { PositionIndicator(scalingLazyListState = scalingLazyListState) @@ -50,16 +67,7 @@ class RouteSelection(private val context: Activity) { var itemIndex by remember { mutableStateOf(1) } ScalingLazyColumn( state = scalingLazyListState, - modifier = Modifier - .fillMaxSize() - .onRotaryScrollEvent { - scope.launch { - scalingLazyListState.animateScrollBy(it.horizontalScrollPixels) - } - true - }, - // .focusRequester(focusRequester) - // .focusable(), + modifier = modifier, contentPadding = PaddingValues(16.dp), autoCentering = AutoCenteringParams(itemIndex = itemIndex) ) { @@ -114,6 +122,11 @@ class RouteSelection(private val context: Activity) { } } } + if (rotaryScrollEnable) { + LaunchedEffect(Unit) { + focusRequester.requestFocus() + } + } } @Composable From f73ddd56569563df9bd348f434b8c56d91e6c95b Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Wed, 14 Dec 2022 03:03:43 +0900 Subject: [PATCH 17/23] First Tile Success --- .../traffic/tiles/components/busRouteText.kt | 19 +++++-- .../traffic/tiles/components/stationText.kt | 6 +- .../tiles/services/BaseStationTileService.kt | 47 ++++++++++++---- .../tiles/services/Station1TileService.kt | 55 +++++++++++++------ .../tiles/services/Station2TileService.kt | 14 +++++ .../kr/yhs/traffic/ui/ComposeSettingTile.kt | 8 ++- 6 files changed, 113 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt index a2087d9..72b9dfc 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt @@ -4,16 +4,16 @@ import androidx.compose.ui.graphics.toArgb import androidx.wear.tiles.ColorBuilders.ColorProp import androidx.wear.tiles.DimensionBuilders.dp import androidx.wear.tiles.DimensionBuilders.sp -import androidx.wear.tiles.LayoutElementBuilders -import androidx.wear.tiles.LayoutElementBuilders.FontStyle -import androidx.wear.tiles.LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END +import androidx.wear.tiles.LayoutElementBuilders.* import androidx.wear.tiles.ModifiersBuilders import androidx.wear.tiles.ModifiersBuilders.Background +import androidx.wear.tiles.ModifiersBuilders.Padding import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.ui.theme.BusColor -fun busRouteText(busInfo: StationRoute) = LayoutElementBuilders.Text.Builder().apply { +@androidx.annotation.OptIn(androidx.wear.tiles.TilesExperimental::class) +fun busRouteText(busInfo: StationRoute) = Text.Builder().apply { var backgroundColor = BusColor.Default for (busColor in BusColor.values()) { if (busInfo.type == busColor.typeCode) { @@ -26,7 +26,8 @@ fun busRouteText(busInfo: StationRoute) = LayoutElementBuilders.Text.Builder().a setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END) setFontStyle( FontStyle.Builder() - .setSize(sp(16f)) + .setSize(sp(19f)) + .setWeight(FONT_WEIGHT_MEDIUM) .build() ) setMaxLines(1) @@ -40,10 +41,16 @@ fun busRouteText(busInfo: StationRoute) = LayoutElementBuilders.Text.Builder().a .build() ).setCorner( ModifiersBuilders.Corner.Builder() - .setRadius(dp(3f)) + .setRadius(dp(12f)) .build() ).build() ) + .setPadding( + Padding.Builder() + .setStart(dp(6f)) + .setEnd(dp(6f)) + .build() + ) .build() ) }.build() diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt index 1f0c3fe..cbb73ae 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt @@ -2,8 +2,7 @@ package kr.yhs.traffic.tiles.components import androidx.wear.tiles.DimensionBuilders.sp import androidx.wear.tiles.LayoutElementBuilders -import androidx.wear.tiles.LayoutElementBuilders.FontStyle -import androidx.wear.tiles.LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END +import androidx.wear.tiles.LayoutElementBuilders.* import kr.yhs.traffic.models.StationInfo @@ -12,7 +11,8 @@ fun stationText(stationInfo: StationInfo) = LayoutElementBuilders.Text.Builder() .setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END) .setFontStyle( FontStyle.Builder() - .setSize(sp(16f)) + .setSize(sp(13f)) + .setWeight(FONT_WEIGHT_BOLD) .build() ).setMaxLines(1) .build() \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt index 3e67cb5..00f4b68 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt @@ -1,11 +1,14 @@ package kr.yhs.traffic.tiles.services import android.content.SharedPreferences +import android.util.Log import androidx.core.content.edit import androidx.wear.tiles.* import androidx.wear.tiles.DimensionBuilders.expand import androidx.wear.tiles.TimelineBuilders.TimelineEntry import com.google.android.horologist.tiles.images.drawableResToImageResource +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.tiles.CoroutinesTileService @@ -15,6 +18,7 @@ import kr.yhs.traffic.tiles.components.clickable import kr.yhs.traffic.utils.ClientBuilder import kr.yhs.traffic.utils.MutableTypeSharedPreferences import kr.yhs.traffic.utils.TrafficClient +import retrofit2.await abstract class BaseStationTileService( private val preferencesId: String, @@ -22,6 +26,12 @@ abstract class BaseStationTileService( ) : CoroutinesTileService(), MutableTypeSharedPreferences { private lateinit var preferences: SharedPreferences private var client: TrafficClient? = null + private val updateId = "UPDATE_BUS_ROUTE" + + var updateClickable = ModifiersBuilders.Clickable.Builder() + .setOnClick( + ActionBuilders.LoadAction.Builder().build() + ).setId(updateId).build() override fun onCreate() { super.onCreate() @@ -36,14 +46,14 @@ abstract class BaseStationTileService( override suspend fun tileRequest(requestParams: RequestBuilders.TileRequest): TileBuilders.Tile = TileBuilders.Tile.Builder().apply { setResourcesVersion(resourcesVersion) - setFreshnessIntervalMillis(1000) + // setFreshnessIntervalMillis(1000) setTimeline( TimelineBuilders.Timeline.Builder() .addTimelineEntry( TimelineEntry.Builder() .setLayout( LayoutElementBuilders.Layout.Builder() - .setRoot(this@BaseStationTileService.tileLayout(requestParams.deviceParameters!!)) + .setRoot(this@BaseStationTileService.tileLayout(requestParams)) .build() ) .build() @@ -61,7 +71,7 @@ abstract class BaseStationTileService( ) }.build() - private suspend fun tileLayout(deviceParameters: DeviceParametersBuilders.DeviceParameters): LayoutElementBuilders.LayoutElement { + private suspend fun tileLayout(requestParams: RequestBuilders.TileRequest): LayoutElementBuilders.LayoutElement { return LayoutElementBuilders.Box.Builder().apply { setHorizontalAlignment(LayoutElementBuilders.HORIZONTAL_ALIGN_CENTER) setWidth(expand()) @@ -76,15 +86,23 @@ abstract class BaseStationTileService( } else { val stationInfo = getStationInfo() val busRouteId = preferences.getStringSet("busRoute", setOf()) - - val routeInfo = /* withContext(Dispatchers.IO) { + val routeInfo = if (requestParams.state?.lastClickableId != updateId) busRouteId?.map { + StationRoute( + it, + preferences.getString("$it-name", null) ?: "알 수 없음", + preferences.getInt("$it-type", 0), + isEnd = false, isWait = false, arrivalInfo = listOf() + ) + } else { client?.getRoute( stationInfo.routeId, stationInfo.type - )?.await() - }?.filter { busRouteId?.contains(it.id) == true } */ null - /* addContent( - stationTileLayout(deviceParameters, stationInfo, routeInfo) - ) */ + )?.await()?.filter { + busRouteId!!.contains(it.id) + } + } + addContent( + stationTileLayout(requestParams.deviceParameters!!, stationInfo, routeInfo) + ) } }.build() } @@ -97,8 +115,17 @@ abstract class BaseStationTileService( override fun onTileRemoveEvent(requestParams: EventBuilders.TileRemoveEvent) { super.onTileRemoveEvent(requestParams) + val busIds = preferences.getStringSet("busRoute", setOf()) preferences.edit { remove("station") + listOf("name", "id", "ids", "posX", "posY", "displayId", "stationId", "type").forEach { + remove("station-$it") + } + busIds?.forEach { + remove("${it}-type") + remove("${it}-name") + } + remove("busRoute") commit() } } diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt index 95e4f77..a05a068 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt @@ -1,11 +1,16 @@ package kr.yhs.traffic.tiles.services import android.util.Log -import androidx.wear.tiles.DeviceParametersBuilders -import androidx.wear.tiles.LayoutElementBuilders +import androidx.compose.ui.graphics.toArgb +import androidx.wear.tiles.* +import androidx.wear.tiles.DimensionBuilders.dp +import androidx.wear.tiles.LayoutElementBuilders.Spacer +import androidx.wear.tiles.ModifiersBuilders.Clickable import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.tiles.components.busRouteText import kr.yhs.traffic.tiles.components.stationText +import kr.yhs.traffic.tiles.textButton class Station1TileService : BaseStationTileService("Station1Tile", "1") { override suspend fun stationTileLayout( @@ -15,20 +20,38 @@ class Station1TileService : BaseStationTileService("Station1Tile", "1") { ): LayoutElementBuilders.LayoutElement = LayoutElementBuilders.Column.Builder() .apply { - // addContent(busRouteText(busRoute!![0])) + addContent(busRouteText(busRoute!![0])) + addContent( + Spacer.Builder() + .setHeight(dp(2f)) + .build() + ) addContent(stationText(stationInfo)) - if (busRoute != null) { - Log.i("bus", busRoute.toString()) - addContent( - LayoutElementBuilders.Text.Builder() - .setText(busRoute[0].name) - .build() - ) - addContent( - LayoutElementBuilders.Text.Builder() - .setText(busRoute[0].arrivalInfo[0].time.toString()) - .build() - ) - } + addContent( + Spacer.Builder() + .setHeight(dp(30f)) + .build() + ) + val time = if (busRoute[0].arrivalInfo.isNotEmpty()) busRoute[0].arrivalInfo[0].time.toString() else "-" + addContent( + LayoutElementBuilders.Text.Builder() + .setText("${time}분") + .setFontStyle( + LayoutElementBuilders.FontStyle.Builder() + .setSize(DimensionBuilders.sp(22f)) + .setWeight(LayoutElementBuilders.FONT_WEIGHT_BOLD) + .build() + ) + .setMaxLines(1) + .build() + ) + addContent( + Spacer.Builder() + .setHeight(dp(30f)) + .build() + ) + addContent( + textButton(this@Station1TileService.baseContext, "조회하기", updateClickable) + ) }.build() } diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt index 549774a..6e010a8 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt @@ -4,6 +4,7 @@ import androidx.wear.tiles.* import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.tiles.components.stationText +import kr.yhs.traffic.tiles.textButton class Station2TileService : BaseStationTileService("Station2Tile", "1") { override suspend fun stationTileLayout( @@ -14,5 +15,18 @@ class Station2TileService : BaseStationTileService("Station2Tile", "1") { = LayoutElementBuilders.Column.Builder() .apply { addContent(stationText(stationInfo)) + addContent( + LayoutElementBuilders.Spacer.Builder() + .setHeight(DimensionBuilders.dp(30f)) + .build() + ) + addContent( + LayoutElementBuilders.Spacer.Builder() + .setHeight(DimensionBuilders.dp(30f)) + .build() + ) + addContent( + textButton(this@Station2TileService.baseContext, "조회하기", ModifiersBuilders.Clickable.Builder().build()) + ) }.build() } diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt index c457a2f..b9d8d32 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt @@ -110,7 +110,13 @@ class ComposeSettingTile( } route = stationRoute preferences.edit { - this.putStringSet("busRoute", stationRoute.map { it.id }.toSet()) + val busIds = mutableSetOf() + stationRoute.forEach { + this.putString("${it.id}-name", it.name) + this.putInt("${it.id}-type", it.type) + busIds.add(it.id) + } + this.putStringSet("busRoute", busIds) } } } From b0927f2158f1bdb93e8250e4acb8c88faf621d07 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Thu, 15 Dec 2022 02:34:32 +0900 Subject: [PATCH 18/23] Tile --- .../tiles/components/busArrivalText.kt | 31 ++++++++++++ .../traffic/tiles/components/busRouteText.kt | 14 ++++-- .../kr/yhs/traffic/tiles/components/spacer.kt | 17 +++++++ .../traffic/tiles/components/stationText.kt | 8 ++- .../tiles/services/BaseStationTileService.kt | 45 ++++++++++++++--- .../tiles/services/Station1TileService.kt | 32 +++--------- .../tiles/services/Station2TileService.kt | 50 +++++++++++++++---- .../kr/yhs/traffic/ui/pages/RouteSelection.kt | 4 +- .../yhs/traffic/ui/pages/StationInfoPage.kt | 9 +--- .../kr/yhs/traffic/utils/timeFormatter.kt | 16 ++++++ 10 files changed, 171 insertions(+), 55 deletions(-) create mode 100644 app/src/main/java/kr/yhs/traffic/tiles/components/busArrivalText.kt create mode 100644 app/src/main/java/kr/yhs/traffic/tiles/components/spacer.kt create mode 100644 app/src/main/java/kr/yhs/traffic/utils/timeFormatter.kt diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/busArrivalText.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/busArrivalText.kt new file mode 100644 index 0000000..1cb01d4 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/busArrivalText.kt @@ -0,0 +1,31 @@ +package kr.yhs.traffic.tiles.components + +import androidx.compose.ui.graphics.toArgb +import androidx.wear.tiles.ColorBuilders.ColorProp +import androidx.wear.tiles.DimensionBuilders.DpProp +import androidx.wear.tiles.DimensionBuilders.SpProp +import androidx.wear.tiles.DimensionBuilders.dp +import androidx.wear.tiles.DimensionBuilders.sp +import androidx.wear.tiles.LayoutElementBuilders +import androidx.wear.tiles.LayoutElementBuilders.* +import androidx.wear.tiles.ModifiersBuilders +import androidx.wear.tiles.ModifiersBuilders.Background +import androidx.wear.tiles.ModifiersBuilders.Padding +import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.ui.theme.BusColor + + +@androidx.annotation.OptIn(androidx.wear.tiles.TilesExperimental::class) +fun busArrivalText( + timeText: String, + fontSize: SpProp = sp(26f) +) = Text.Builder().apply { + setText(timeText) + setFontStyle( + FontStyle.Builder() + .setSize(fontSize) + .setWeight(FONT_WEIGHT_BOLD) + .build() + ) + setMaxLines(1) +}.build() diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt index 72b9dfc..4ec92e1 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/busRouteText.kt @@ -2,6 +2,8 @@ package kr.yhs.traffic.tiles.components import androidx.compose.ui.graphics.toArgb import androidx.wear.tiles.ColorBuilders.ColorProp +import androidx.wear.tiles.DimensionBuilders.DpProp +import androidx.wear.tiles.DimensionBuilders.SpProp import androidx.wear.tiles.DimensionBuilders.dp import androidx.wear.tiles.DimensionBuilders.sp import androidx.wear.tiles.LayoutElementBuilders.* @@ -13,7 +15,11 @@ import kr.yhs.traffic.ui.theme.BusColor @androidx.annotation.OptIn(androidx.wear.tiles.TilesExperimental::class) -fun busRouteText(busInfo: StationRoute) = Text.Builder().apply { +fun busRouteText( + busInfo: StationRoute, + fontSize: SpProp = sp(19f), + padding: DpProp = dp(6f) +) = Text.Builder().apply { var backgroundColor = BusColor.Default for (busColor in BusColor.values()) { if (busInfo.type == busColor.typeCode) { @@ -26,7 +32,7 @@ fun busRouteText(busInfo: StationRoute) = Text.Builder().apply { setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END) setFontStyle( FontStyle.Builder() - .setSize(sp(19f)) + .setSize(fontSize) .setWeight(FONT_WEIGHT_MEDIUM) .build() ) @@ -47,8 +53,8 @@ fun busRouteText(busInfo: StationRoute) = Text.Builder().apply { ) .setPadding( Padding.Builder() - .setStart(dp(6f)) - .setEnd(dp(6f)) + .setStart(padding) + .setEnd(padding) .build() ) .build() diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/spacer.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/spacer.kt new file mode 100644 index 0000000..8f273e9 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/spacer.kt @@ -0,0 +1,17 @@ +package kr.yhs.traffic.tiles.components + +import androidx.wear.tiles.DimensionBuilders +import androidx.wear.tiles.DimensionBuilders.SpProp +import androidx.wear.tiles.DimensionBuilders.sp +import androidx.wear.tiles.LayoutElementBuilders +import androidx.wear.tiles.LayoutElementBuilders.* +import kr.yhs.traffic.models.StationInfo + + +fun spacer( + width: DimensionBuilders.DpProp? = null, + height: DimensionBuilders.DpProp? = null +) = Spacer.Builder().apply { + if (width != null) setWidth(width) + if (height != null) setHeight(height) +}.build() \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt index cbb73ae..04462b9 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt @@ -1,17 +1,21 @@ package kr.yhs.traffic.tiles.components +import androidx.wear.tiles.DimensionBuilders.SpProp import androidx.wear.tiles.DimensionBuilders.sp import androidx.wear.tiles.LayoutElementBuilders import androidx.wear.tiles.LayoutElementBuilders.* import kr.yhs.traffic.models.StationInfo -fun stationText(stationInfo: StationInfo) = LayoutElementBuilders.Text.Builder() +fun stationText( + stationInfo: StationInfo, + fontSize: SpProp = sp(13f) +) = Text.Builder() .setText(stationInfo.name) .setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END) .setFontStyle( FontStyle.Builder() - .setSize(sp(13f)) + .setSize(fontSize) .setWeight(FONT_WEIGHT_BOLD) .build() ).setMaxLines(1) diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt index 00f4b68..b94258f 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt @@ -1,14 +1,16 @@ package kr.yhs.traffic.tiles.services import android.content.SharedPreferences -import android.util.Log import androidx.core.content.edit import androidx.wear.tiles.* import androidx.wear.tiles.DimensionBuilders.expand import androidx.wear.tiles.TimelineBuilders.TimelineEntry import com.google.android.horologist.tiles.images.drawableResToImageResource +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import kr.yhs.traffic.R +import kr.yhs.traffic.models.ArrivalInfo import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.tiles.CoroutinesTileService @@ -18,7 +20,10 @@ import kr.yhs.traffic.tiles.components.clickable import kr.yhs.traffic.utils.ClientBuilder import kr.yhs.traffic.utils.MutableTypeSharedPreferences import kr.yhs.traffic.utils.TrafficClient +import kr.yhs.traffic.utils.timeFormatter +import retrofit2.HttpException import retrofit2.await +import java.net.SocketTimeoutException abstract class BaseStationTileService( private val preferencesId: String, @@ -86,18 +91,21 @@ abstract class BaseStationTileService( } else { val stationInfo = getStationInfo() val busRouteId = preferences.getStringSet("busRoute", setOf()) - val routeInfo = if (requestParams.state?.lastClickableId != updateId) busRouteId?.map { + val defaultBusRouteInfo = busRouteId?.map { StationRoute( it, preferences.getString("$it-name", null) ?: "알 수 없음", preferences.getInt("$it-type", 0), isEnd = false, isWait = false, arrivalInfo = listOf() ) - } else { - client?.getRoute( - stationInfo.routeId, stationInfo.type - )?.await()?.filter { - busRouteId!!.contains(it.id) + } + val routeInfo = if (requestParams.state?.lastClickableId != updateId) defaultBusRouteInfo else { + try { + getRoute(Dispatchers.IO, stationInfo)?.filter { + busRouteId!!.contains(it.id) + } ?: defaultBusRouteInfo + } catch (e: Exception) { + defaultBusRouteInfo } } addContent( @@ -107,6 +115,15 @@ abstract class BaseStationTileService( }.build() } + suspend fun getRoute( + dispatcher: CoroutineDispatcher, + stationInfo: StationInfo + ) = withContext(dispatcher) { + client?.getRoute( + stationInfo.routeId, stationInfo.type + )?.await() + } + abstract suspend fun stationTileLayout( deviceParameters: DeviceParametersBuilders.DeviceParameters, stationInfo: StationInfo, @@ -130,6 +147,20 @@ abstract class BaseStationTileService( } } + fun getArrivalText(routeInfo: StationRoute): String { + return when { + routeInfo.isEnd == true -> "운행 종료" + routeInfo.isWait == true -> "운행 대기" + routeInfo.arrivalInfo.isNotEmpty() -> { + val arrivalInfo = routeInfo.arrivalInfo[0] + if (arrivalInfo.prevCount == 0 && arrivalInfo.time <= 180 || arrivalInfo.time <= 60) + this@BaseStationTileService.getString(R.string.arrival_text_soon) + else timeFormatter(this, routeInfo.arrivalInfo[0].time, false) + } + else -> "-분" + } + } + private fun getStationInfo() = StationInfo( this.preferences.getString("station-name", "알 수 없음") ?: "알 수 없음", this.preferences.getString("station-id", null) ?: "-2", diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt index a05a068..959e86b 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt @@ -1,14 +1,14 @@ package kr.yhs.traffic.tiles.services -import android.util.Log -import androidx.compose.ui.graphics.toArgb import androidx.wear.tiles.* import androidx.wear.tiles.DimensionBuilders.dp +import androidx.wear.tiles.DimensionBuilders.sp import androidx.wear.tiles.LayoutElementBuilders.Spacer -import androidx.wear.tiles.ModifiersBuilders.Clickable import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.tiles.components.busArrivalText import kr.yhs.traffic.tiles.components.busRouteText +import kr.yhs.traffic.tiles.components.spacer import kr.yhs.traffic.tiles.components.stationText import kr.yhs.traffic.tiles.textButton @@ -20,35 +20,19 @@ class Station1TileService : BaseStationTileService("Station1Tile", "1") { ): LayoutElementBuilders.LayoutElement = LayoutElementBuilders.Column.Builder() .apply { - addContent(busRouteText(busRoute!![0])) + addContent(busRouteText(busRoute!![0], sp(19f), dp(6f))) addContent( - Spacer.Builder() - .setHeight(dp(2f)) - .build() + spacer(height = dp(2f)) ) addContent(stationText(stationInfo)) addContent( - Spacer.Builder() - .setHeight(dp(30f)) - .build() + spacer(height = dp(30f)) ) - val time = if (busRoute[0].arrivalInfo.isNotEmpty()) busRoute[0].arrivalInfo[0].time.toString() else "-" addContent( - LayoutElementBuilders.Text.Builder() - .setText("${time}분") - .setFontStyle( - LayoutElementBuilders.FontStyle.Builder() - .setSize(DimensionBuilders.sp(22f)) - .setWeight(LayoutElementBuilders.FONT_WEIGHT_BOLD) - .build() - ) - .setMaxLines(1) - .build() + busArrivalText(this@Station1TileService.getArrivalText(busRoute[0])) ) addContent( - Spacer.Builder() - .setHeight(dp(30f)) - .build() + spacer(height = dp(30f)) ) addContent( textButton(this@Station1TileService.baseContext, "조회하기", updateClickable) diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt index 6e010a8..1e8bbf0 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt @@ -1,8 +1,15 @@ package kr.yhs.traffic.tiles.services import androidx.wear.tiles.* +import androidx.wear.tiles.DimensionBuilders.dp +import androidx.wear.tiles.DimensionBuilders.sp +import androidx.wear.tiles.LayoutElementBuilders.VERTICAL_ALIGN_CENTER +import androidx.wear.tiles.LayoutElementBuilders.VerticalAlignmentProp import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.tiles.components.busArrivalText +import kr.yhs.traffic.tiles.components.busRouteText +import kr.yhs.traffic.tiles.components.spacer import kr.yhs.traffic.tiles.components.stationText import kr.yhs.traffic.tiles.textButton @@ -10,23 +17,48 @@ class Station2TileService : BaseStationTileService("Station2Tile", "1") { override suspend fun stationTileLayout( deviceParameters: DeviceParametersBuilders.DeviceParameters, stationInfo: StationInfo, - busRoute: List? + routeInfo: List? ): LayoutElementBuilders.LayoutElement = LayoutElementBuilders.Column.Builder() .apply { - addContent(stationText(stationInfo)) + addContent(stationText(stationInfo, sp(14f))) addContent( - LayoutElementBuilders.Spacer.Builder() - .setHeight(DimensionBuilders.dp(30f)) - .build() + spacer(height = dp(30f)) ) addContent( - LayoutElementBuilders.Spacer.Builder() - .setHeight(DimensionBuilders.dp(30f)) - .build() + stationTileRow(routeInfo!!) ) addContent( - textButton(this@Station2TileService.baseContext, "조회하기", ModifiersBuilders.Clickable.Builder().build()) + spacer(height = dp(30f)) + ) + addContent( + textButton(this@Station2TileService.baseContext, "조회하기", updateClickable) ) }.build() + + private fun stationTileRow( + busRoute: List + ) = LayoutElementBuilders.Row.Builder() + .apply { + setVerticalAlignment( + VerticalAlignmentProp + .Builder() + .setValue(VERTICAL_ALIGN_CENTER) + .build() + ) + addContent(arrivalTileLayout(busRoute[0])) + addContent(spacer(width = dp(20f))) + addContent(arrivalTileLayout(busRoute[1])) + }.build() + + private fun arrivalTileLayout( + busRoute: StationRoute + ) = LayoutElementBuilders.Column.Builder().apply { + addContent ( + busRouteText(busRoute, sp(14f), dp(4f)) + ) + addContent ( + busArrivalText(this@Station2TileService.getArrivalText(busRoute)) + ) + }.build() } diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt index 71dbc68..62baa96 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt @@ -110,10 +110,10 @@ class RouteSelection(private val context: Activity) { NextButton( modifier = Modifier.padding(top = 30.dp) ) { - if (checkedRoute.size < 1) { + if (checkedRoute.size == maxSelect) { ConfirmationOverlay() .setType(ConfirmationOverlay.FAILURE_ANIMATION) - .setMessage("최소 1개 이상 선택해 주세요.") + .setMessage("${maxSelect}개의 버스 노선을 선택해 주세요.") .showOn(context) return@NextButton } diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt index ff9ed90..5c66895 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt @@ -28,6 +28,7 @@ import kr.yhs.traffic.ui.components.LoadingProgressIndicator import kr.yhs.traffic.utils.StopWatch import kr.yhs.traffic.ui.theme.BusColor import kr.yhs.traffic.ui.theme.StationInfoSelection +import kr.yhs.traffic.utils.timeFormatter @OptIn(ExperimentalComposeUiApi::class) @Composable @@ -198,13 +199,7 @@ fun StationRoute( if (timeMillis == -1 || arrivalInfo.prevCount == null) continue - time = when { - timeMillis / 60 < 1 -> context.getString(R.string.timestamp_second, timeMillis) - timeMillis / 3600 < 1 && (timeMillis % 60 == 0 || (busInfo.type in 1200..1299) || (busInfo.type in 2100..2199)) -> context.getString(R.string.timestamp_minute, timeMillis / 60) - timeMillis / 3600 < 1 && (busInfo.type in 1100..1199 || busInfo.type in 1300..1399) -> context.getString(R.string.timestamp_minute_second, timeMillis / 60, timeMillis % 60) - timeMillis / 216000 < 1 -> context.getString(R.string.timestamp_hour_minute, timeMillis / 3600, timeMillis % 3600 / 60) - else -> context.getString(R.string.timestamp_second, timeMillis) - } + time = timeFormatter(context, timeMillis, (busInfo.type in 1100..1199 || busInfo.type in 1300..1399)) timeMillis = arrivalInfo.time - (timeLoop / 1000) var response: String? = null diff --git a/app/src/main/java/kr/yhs/traffic/utils/timeFormatter.kt b/app/src/main/java/kr/yhs/traffic/utils/timeFormatter.kt new file mode 100644 index 0000000..c693618 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/utils/timeFormatter.kt @@ -0,0 +1,16 @@ +package kr.yhs.traffic.utils + +import android.content.Context +import kr.yhs.traffic.R + +fun timeFormatter (context: Context, timeMillis: Int, detail: Boolean): String = when { + timeMillis / 60 < 1 -> + context.getString(R.string.timestamp_second, timeMillis) + timeMillis / 3600 < 1 && (timeMillis % 60 == 0 || !detail) -> + context.getString(R.string.timestamp_minute, timeMillis / 60) + timeMillis / 3600 < 1 && detail -> context.getString( + R.string.timestamp_minute_second, timeMillis / 60, timeMillis % 60) + timeMillis / 216000 < 1 -> + context.getString(R.string.timestamp_hour_minute, timeMillis / 3600, timeMillis % 3600 / 60) + else -> context.getString(R.string.timestamp_second, timeMillis) +} \ No newline at end of file From f2a1b14fdffaa42e51bd21a9c0a1a85c274ab79b Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Thu, 15 Dec 2022 02:52:25 +0900 Subject: [PATCH 19/23] Tile (set padding) --- .../tiles/services/Station2TileService.kt | 18 ++++++++++-------- .../kr/yhs/traffic/ui/pages/RouteSelection.kt | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt index 1e8bbf0..7fba6d1 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt @@ -1,8 +1,7 @@ package kr.yhs.traffic.tiles.services import androidx.wear.tiles.* -import androidx.wear.tiles.DimensionBuilders.dp -import androidx.wear.tiles.DimensionBuilders.sp +import androidx.wear.tiles.DimensionBuilders.* import androidx.wear.tiles.LayoutElementBuilders.VERTICAL_ALIGN_CENTER import androidx.wear.tiles.LayoutElementBuilders.VerticalAlignmentProp import kr.yhs.traffic.models.StationInfo @@ -26,7 +25,7 @@ class Station2TileService : BaseStationTileService("Station2Tile", "1") { spacer(height = dp(30f)) ) addContent( - stationTileRow(routeInfo!!) + stationTileRow(deviceParameters, routeInfo!!) ) addContent( spacer(height = dp(30f)) @@ -37,6 +36,7 @@ class Station2TileService : BaseStationTileService("Station2Tile", "1") { }.build() private fun stationTileRow( + deviceParameters: DeviceParametersBuilders.DeviceParameters, busRoute: List ) = LayoutElementBuilders.Row.Builder() .apply { @@ -46,19 +46,21 @@ class Station2TileService : BaseStationTileService("Station2Tile", "1") { .setValue(VERTICAL_ALIGN_CENTER) .build() ) - addContent(arrivalTileLayout(busRoute[0])) - addContent(spacer(width = dp(20f))) - addContent(arrivalTileLayout(busRoute[1])) + addContent(arrivalTileLayout(busRoute[0], dp(((deviceParameters.screenWidthDp - 10) / 2).toFloat()))) + addContent(spacer(width = dp(10f))) + addContent(arrivalTileLayout(busRoute[1], dp(((deviceParameters.screenWidthDp - 10) / 2).toFloat()))) }.build() private fun arrivalTileLayout( - busRoute: StationRoute + busRoute: StationRoute, + width: DpProp = dp(80f) ) = LayoutElementBuilders.Column.Builder().apply { + setWidth(width) addContent ( busRouteText(busRoute, sp(14f), dp(4f)) ) addContent ( - busArrivalText(this@Station2TileService.getArrivalText(busRoute)) + busArrivalText(this@Station2TileService.getArrivalText(busRoute), sp(22f)) ) }.build() } diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt index 62baa96..05b9a77 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt @@ -110,7 +110,7 @@ class RouteSelection(private val context: Activity) { NextButton( modifier = Modifier.padding(top = 30.dp) ) { - if (checkedRoute.size == maxSelect) { + if (checkedRoute.size < maxSelect) { ConfirmationOverlay() .setType(ConfirmationOverlay.FAILURE_ANIMATION) .setMessage("${maxSelect}개의 버스 노선을 선택해 주세요.") From 68d69e1ba7c2325a29c6ae691a9164e5e315e9c9 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Fri, 16 Dec 2022 02:19:51 +0900 Subject: [PATCH 20/23] StationText Button --- app/src/main/AndroidManifest.xml | 4 + .../kr/yhs/traffic/SettingTileActivity.kt | 6 +- .../kr/yhs/traffic/StationInfoActivity.kt | 36 ++++++ .../java/kr/yhs/traffic/StationTileType.kt | 15 +++ app/src/main/java/kr/yhs/traffic/TileType.kt | 10 +- .../yhs/traffic/tiles/components/clickable.kt | 9 +- .../traffic/tiles/components/stationText.kt | 33 +++-- .../traffic/tiles/components/textButton.kt | 2 +- .../tiles/services/BaseStationTileService.kt | 34 ++--- .../tiles/services/Station1TileService.kt | 9 +- .../tiles/services/Station2TileService.kt | 3 +- .../java/kr/yhs/traffic/ui/BaseCompose.kt | 15 ++- .../yhs/traffic/ui/BaseComposeStationInfo.kt | 111 +++++++++++++++++ .../main/java/kr/yhs/traffic/ui/ComposeApp.kt | 117 ++---------------- .../kr/yhs/traffic/ui/ComposeSettingTile.kt | 31 +++-- .../kr/yhs/traffic/ui/ComposeStationInfo.kt | 26 ++++ .../kr/yhs/traffic/ui/pages/RouteSelection.kt | 31 +++-- .../java/kr/yhs/traffic/ui/pages/StepPage.kt | 42 +++++-- .../pages/{navigator => pager}/StationGPS.kt | 2 +- .../{navigator => pager}/StationSearch.kt | 2 +- .../pages/{navigator => pager}/StationStar.kt | 2 +- .../yhs/traffic/utils/StationPreferences.kt | 17 +++ 22 files changed, 356 insertions(+), 201 deletions(-) create mode 100644 app/src/main/java/kr/yhs/traffic/StationInfoActivity.kt create mode 100644 app/src/main/java/kr/yhs/traffic/StationTileType.kt create mode 100644 app/src/main/java/kr/yhs/traffic/ui/BaseComposeStationInfo.kt create mode 100644 app/src/main/java/kr/yhs/traffic/ui/ComposeStationInfo.kt rename app/src/main/java/kr/yhs/traffic/ui/pages/{navigator => pager}/StationGPS.kt (97%) rename app/src/main/java/kr/yhs/traffic/ui/pages/{navigator => pager}/StationSearch.kt (98%) rename app/src/main/java/kr/yhs/traffic/ui/pages/{navigator => pager}/StationStar.kt (97%) create mode 100644 app/src/main/java/kr/yhs/traffic/utils/StationPreferences.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee22d9c..32f4cdd 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,6 +53,10 @@ android:name=".SettingTileActivity" android:exported="true" /> + + , - val maxBusSelect: Int + open val preferenceId: String, + open val classJava: Class, ) { - object Station1: TileType("실시간 버스 정보(1개)", "Station1Tile", Station1TileService::class.java, 1) - object Station2: TileType("실시간 버스 정보(2개)", "Station2Tile", Station2TileService::class.java, 2) + object Station1: TileType("Station1Tile", Station1TileService::class.java) + object Station2: TileType("Station2Tile", Station2TileService::class.java) val id: String get() = this.classJava.name diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/clickable.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/clickable.kt index 175fd7f..efbdfe1 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/components/clickable.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/clickable.kt @@ -3,15 +3,16 @@ package kr.yhs.traffic.tiles.components import androidx.wear.tiles.ActionBuilders import androidx.wear.tiles.ModifiersBuilders import androidx.wear.tiles.TileService -import kr.yhs.traffic.SettingTileActivity -fun clickable(classPackage: TileService) = ModifiersBuilders.Clickable.Builder() - .setId(classPackage::class.java.name) +fun clickable(className: String, classPackage: TileService) = clickable(className, classPackage, classPackage::class.java.name) + +fun clickable(className: String, classPackage: TileService, id: String) = ModifiersBuilders.Clickable.Builder() + .setId(id) .setOnClick( ActionBuilders.LaunchAction.Builder() .setAndroidActivity( ActionBuilders.AndroidActivity.Builder() - .setClassName(SettingTileActivity::class.java.name) + .setClassName(className) .setPackageName(classPackage.packageName) .build() ).build() diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt index 04462b9..de6abaf 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/stationText.kt @@ -4,19 +4,32 @@ import androidx.wear.tiles.DimensionBuilders.SpProp import androidx.wear.tiles.DimensionBuilders.sp import androidx.wear.tiles.LayoutElementBuilders import androidx.wear.tiles.LayoutElementBuilders.* +import androidx.wear.tiles.ModifiersBuilders +import androidx.wear.tiles.ModifiersBuilders.Clickable +import androidx.wear.tiles.ModifiersBuilders.Modifiers import kr.yhs.traffic.models.StationInfo fun stationText( stationInfo: StationInfo, - fontSize: SpProp = sp(13f) + fontSize: SpProp = sp(13f), + clickable: Clickable? = null ) = Text.Builder() - .setText(stationInfo.name) - .setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END) - .setFontStyle( - FontStyle.Builder() - .setSize(fontSize) - .setWeight(FONT_WEIGHT_BOLD) - .build() - ).setMaxLines(1) - .build() \ No newline at end of file + .apply { + if (clickable != null) { + setModifiers( + Modifiers.Builder() + .setClickable(clickable) + .build() + ) + } + setText(stationInfo.name) + setOverflow(TEXT_OVERFLOW_ELLIPSIZE_END) + setFontStyle( + FontStyle.Builder() + .setSize(fontSize) + .setWeight(FONT_WEIGHT_BOLD) + .build() + ) + setMaxLines(1) + }.build() \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/tiles/components/textButton.kt b/app/src/main/java/kr/yhs/traffic/tiles/components/textButton.kt index 76ddbd4..11f86c9 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/components/textButton.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/components/textButton.kt @@ -26,7 +26,7 @@ fun textButton( italic: Boolean = false, overflow: Int = LayoutElementBuilders.TEXT_OVERFLOW_ELLIPSIZE_END, multilineAlignment: Int = TEXT_ALIGN_CENTER -) = LayoutElementBuilders.Box.Builder().apply { +) = Box.Builder().apply { setWidth(WIDTH_SIZE) setHeight(HEIGHT_SIZE) setModifiers( diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt index b94258f..d7e9d2e 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt @@ -1,6 +1,7 @@ package kr.yhs.traffic.tiles.services import android.content.SharedPreferences +import android.util.Log import androidx.core.content.edit import androidx.wear.tiles.* import androidx.wear.tiles.DimensionBuilders.expand @@ -10,25 +11,21 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kr.yhs.traffic.R -import kr.yhs.traffic.models.ArrivalInfo +import kr.yhs.traffic.SettingTileActivity +import kr.yhs.traffic.StationInfoActivity import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.tiles.CoroutinesTileService import kr.yhs.traffic.tiles.ImageId import kr.yhs.traffic.tiles.components.SettingRequirement import kr.yhs.traffic.tiles.components.clickable -import kr.yhs.traffic.utils.ClientBuilder -import kr.yhs.traffic.utils.MutableTypeSharedPreferences -import kr.yhs.traffic.utils.TrafficClient -import kr.yhs.traffic.utils.timeFormatter -import retrofit2.HttpException +import kr.yhs.traffic.utils.* import retrofit2.await -import java.net.SocketTimeoutException abstract class BaseStationTileService( private val preferencesId: String, private val resourcesVersion: String -) : CoroutinesTileService(), MutableTypeSharedPreferences { +) : CoroutinesTileService(), StationPreferences { private lateinit var preferences: SharedPreferences private var client: TrafficClient? = null private val updateId = "UPDATE_BUS_ROUTE" @@ -37,6 +34,8 @@ abstract class BaseStationTileService( .setOnClick( ActionBuilders.LoadAction.Builder().build() ).setId(updateId).build() + val stationClickable + get() = clickable(StationInfoActivity::class.java.name, this@BaseStationTileService) override fun onCreate() { super.onCreate() @@ -72,7 +71,7 @@ abstract class BaseStationTileService( setVersion(resourcesVersion) addIdToImageMapping( ImageId.Logo.id, - drawableResToImageResource(kr.yhs.traffic.R.mipmap.ic_launcher) + drawableResToImageResource(R.mipmap.ic_launcher) ) }.build() @@ -84,12 +83,12 @@ abstract class BaseStationTileService( if (!preferences.contains("station")) { addContent ( SettingRequirement(this@BaseStationTileService.baseContext).content( - "도착 예정 버스", "도착할 버스 정보를 불러오기 위한 버스 정류장을 등록해주세요.", - clickable(this@BaseStationTileService) + "실시간 버스 정보", "도착할 버스 정보를 불러오기 위한 버스 정류장을 등록해주세요.", + clickable(SettingTileActivity::class.java.name, this@BaseStationTileService) ) ) } else { - val stationInfo = getStationInfo() + val stationInfo = getStationInfo(preferences) val busRouteId = preferences.getStringSet("busRoute", setOf()) val defaultBusRouteInfo = busRouteId?.map { StationRoute( @@ -160,15 +159,4 @@ abstract class BaseStationTileService( else -> "-분" } } - - private fun getStationInfo() = StationInfo( - this.preferences.getString("station-name", "알 수 없음") ?: "알 수 없음", - this.preferences.getString("station-id", null) ?: "-2", - this.preferences.getString("station-ids", null), - this.preferences.getFloat("station-posX", 0.0F).toDouble(), - this.preferences.getFloat("station-posY", 0.0F).toDouble(), - this.preferences.getString("station-displayId", null) ?: "0", - getMutableType(this.preferences, "station-stationId", null) ?: "0", - this.preferences.getInt("station-type", 0), - ) } diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt index 959e86b..1a8442e 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt @@ -4,12 +4,11 @@ import androidx.wear.tiles.* import androidx.wear.tiles.DimensionBuilders.dp import androidx.wear.tiles.DimensionBuilders.sp import androidx.wear.tiles.LayoutElementBuilders.Spacer +import kr.yhs.traffic.SettingTileActivity +import kr.yhs.traffic.StationInfoActivity import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute -import kr.yhs.traffic.tiles.components.busArrivalText -import kr.yhs.traffic.tiles.components.busRouteText -import kr.yhs.traffic.tiles.components.spacer -import kr.yhs.traffic.tiles.components.stationText +import kr.yhs.traffic.tiles.components.* import kr.yhs.traffic.tiles.textButton class Station1TileService : BaseStationTileService("Station1Tile", "1") { @@ -24,7 +23,7 @@ class Station1TileService : BaseStationTileService("Station1Tile", "1") { addContent( spacer(height = dp(2f)) ) - addContent(stationText(stationInfo)) + addContent(stationText(stationInfo, clickable = stationClickable)) addContent( spacer(height = dp(30f)) ) diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt index 7fba6d1..fc06b27 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt @@ -1,5 +1,6 @@ package kr.yhs.traffic.tiles.services +import android.util.Log import androidx.wear.tiles.* import androidx.wear.tiles.DimensionBuilders.* import androidx.wear.tiles.LayoutElementBuilders.VERTICAL_ALIGN_CENTER @@ -20,7 +21,7 @@ class Station2TileService : BaseStationTileService("Station2Tile", "1") { ): LayoutElementBuilders.LayoutElement = LayoutElementBuilders.Column.Builder() .apply { - addContent(stationText(stationInfo, sp(14f))) + addContent(stationText(stationInfo, sp(14f), clickable = stationClickable)) addContent( spacer(height = dp(30f)) ) diff --git a/app/src/main/java/kr/yhs/traffic/ui/BaseCompose.kt b/app/src/main/java/kr/yhs/traffic/ui/BaseCompose.kt index 6258abf..3b6be41 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/BaseCompose.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/BaseCompose.kt @@ -1,16 +1,25 @@ package kr.yhs.traffic.ui import android.content.SharedPreferences -import androidx.compose.runtime.Composable -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.withContext +import androidx.compose.runtime.* +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.edit +import kotlinx.coroutines.* +import kr.yhs.traffic.R import kr.yhs.traffic.models.StationInfo +import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.ui.pages.StationInfoPage +import kr.yhs.traffic.ui.theme.StationInfoSelection import kr.yhs.traffic.utils.MutableTypeSharedPreferences import kr.yhs.traffic.utils.TrafficClient +import retrofit2.HttpException import retrofit2.await +import java.net.SocketTimeoutException abstract class BaseCompose(private val client: TrafficClient?): MutableTypeSharedPreferences { + val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO + @Composable abstract fun Content() diff --git a/app/src/main/java/kr/yhs/traffic/ui/BaseComposeStationInfo.kt b/app/src/main/java/kr/yhs/traffic/ui/BaseComposeStationInfo.kt new file mode 100644 index 0000000..44c6401 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/ui/BaseComposeStationInfo.kt @@ -0,0 +1,111 @@ +package kr.yhs.traffic.ui + +import android.app.Activity +import androidx.compose.runtime.* +import androidx.core.content.edit +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kr.yhs.traffic.R +import kr.yhs.traffic.models.StationInfo +import kr.yhs.traffic.models.StationRoute +import kr.yhs.traffic.ui.pages.StationInfoPage +import kr.yhs.traffic.ui.theme.StationInfoSelection +import kr.yhs.traffic.utils.TrafficClient +import retrofit2.HttpException +import java.net.SocketTimeoutException + + +abstract class BaseComposeStationInfo( + private val activity: Activity, + client: TrafficClient? +) : BaseCompose(client) { + @Composable + fun ComposeStationInfoPage( + station: StationInfo, + scope: CoroutineScope, + onFailed: (CharSequence) -> Unit + ) { + var busList by remember { mutableStateOf>(emptyList()) } + var isLoading by remember { mutableStateOf(true) } + + LaunchedEffect(true) { + try { + busList = getRoute( + defaultDispatcher, + station.routeId, + station.type + ) + isLoading = false + } catch (e: Exception) { + when (e) { + is SocketTimeoutException, is HttpException -> { + onFailed(activity.getText(R.string.timeout)) + return@LaunchedEffect + } + else -> throw e + } + } + } + val sharedPreferences = getPreferences("bookmark") + val bookmark = sharedPreferences.getStringSet("bookmark-list", mutableSetOf())?: mutableSetOf() + val bookmarkKey = "${station.routeId}0${station.type}" + + StationInfoPage( + station, busList, + bookmark.contains(bookmarkKey), isLoading, scope + ) { + when (it) { + StationInfoSelection.BOOKMARK -> { + val newBookmark = mutableSetOf() + newBookmark.addAll(bookmark.toMutableSet()) + if (bookmark.contains(bookmarkKey)) { + listOf( + "name", "type", "id", "ids", "posX", "posY", "stationId", "displayId" + ).forEach { + sharedPreferences.edit { + if (sharedPreferences.contains("$bookmarkKey-$it-value")) + removeMutableType(this, "$bookmarkKey-$it") + else + remove("$bookmarkKey-$it") + } + } + newBookmark.remove(bookmarkKey) + sharedPreferences.edit { + putStringSet("bookmark-list", newBookmark) + commit() + } + } else { + val displayId = if (station.displayId is List<*>) { + station.displayId.joinToString(", ") + } else station.displayId?.toString() ?: " " + newBookmark.add(bookmarkKey) + sharedPreferences.edit { + putString("$bookmarkKey-name", station.name) + putInt("$bookmarkKey-type", station.type) + putString("$bookmarkKey-id", station.id) + putString("$bookmarkKey-ids", station.ids) + putFloat("$bookmarkKey-posX", station.posX.toFloat()) + putFloat("$bookmarkKey-posY", station.posY.toFloat()) + putMutableType( + this, + "$bookmarkKey-stationId", + station.stationId + ) + putString("$bookmarkKey-displayId", displayId) + putStringSet("bookmark-list", newBookmark) + commit() + } + } + } + StationInfoSelection.REFRESH -> { + scope.launch { + try { + busList = getRoute(Dispatchers.Default, station) + } catch (_: SocketTimeoutException) {} + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt index 937c4e1..6faffd9 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt @@ -13,7 +13,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.core.app.ActivityCompat -import androidx.core.content.edit import androidx.navigation.NavController import androidx.navigation.NavType import androidx.navigation.navArgument @@ -29,23 +28,16 @@ import kr.yhs.traffic.* import kr.yhs.traffic.R import kr.yhs.traffic.models.DropdownQuery import kr.yhs.traffic.models.StationInfo -import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.ui.components.AccompanistPager import kr.yhs.traffic.ui.pages.* -import kr.yhs.traffic.ui.pages.navigator.StationGPS -import kr.yhs.traffic.ui.pages.navigator.StationSearch -import kr.yhs.traffic.ui.pages.navigator.StationStar -import kr.yhs.traffic.ui.theme.StationInfoSelection -import kr.yhs.traffic.utils.MutableTypeSharedPreferences +import kr.yhs.traffic.ui.pages.pager.StationGPS +import kr.yhs.traffic.ui.pages.pager.StationSearch +import kr.yhs.traffic.ui.pages.pager.StationStar import kr.yhs.traffic.utils.getLocation -import retrofit2.HttpException -import retrofit2.await import java.net.SocketTimeoutException -class ComposeApp(private val activity: MainActivity): BaseCompose(activity.client) { - private val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO - +class ComposeApp(private val activity: MainActivity): BaseComposeStationInfo(activity, activity.client) { override fun getPreferences(filename: String): SharedPreferences = activity.getPreferences(filename) @OptIn( @@ -134,8 +126,6 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien composable( Screen.StationInfo.route ) { - var busList by remember { mutableStateOf>(emptyList()) } - var isLoading by remember { mutableStateOf(true) } if (lastStation == null) { ConfirmationOverlay() .setType(ConfirmationOverlay.FAILURE_ANIMATION) @@ -144,97 +134,13 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien navigationController.popBackStack() return@composable } - val station = lastStation!! - LaunchedEffect(true) { - try { - busList = getRoute( - defaultDispatcher, - station.routeId, - station.type - ) - isLoading = false - } catch (e: Exception) { - when (e) { - is SocketTimeoutException, is HttpException -> { - ConfirmationOverlay() - .setType(ConfirmationOverlay.FAILURE_ANIMATION) - .setMessage(activity.getText(R.string.timeout)) - .showOn(activity) - navigationController.popBackStack() - return@LaunchedEffect - } - else -> throw e - } - } - // Log.i("BusInfo", "$busList") - } - val sharedPreferences = getPreferences("bookmark") - val bookmark = sharedPreferences.getStringSet("bookmark-list", mutableSetOf()) - ?: mutableSetOf() - val bookmarkKey = "${station.routeId}0${station.type}" - - StationInfoPage( - station, busList, - bookmark.contains(bookmarkKey), isLoading, scope - ) { - when (it) { - StationInfoSelection.BOOKMARK -> { - val newBookmark = mutableSetOf() - newBookmark.addAll(bookmark.toMutableSet()) - if (bookmark.contains(bookmarkKey)) { - listOf( - "name", - "type", - "id", - "ids", - "posX", - "posY", - "stationId", - "displayId" - ).forEach { - sharedPreferences.edit { - if (sharedPreferences.contains("$bookmarkKey-$it-value")) - removeMutableType(this, "$bookmarkKey-$it") - else - remove("$bookmarkKey-$it") - } - } - newBookmark.remove(bookmarkKey) - sharedPreferences.edit { - putStringSet("bookmark-list", newBookmark) - commit() - } - } else { - val displayId = if (station.displayId is List<*>) { - station.displayId.joinToString(", ") - } else station.displayId?.toString() ?: " " - newBookmark.add(bookmarkKey) - sharedPreferences.edit { - putString("$bookmarkKey-name", station.name) - putInt("$bookmarkKey-type", station.type) - putString("$bookmarkKey-id", station.id) - putString("$bookmarkKey-ids", station.ids) - putFloat("$bookmarkKey-posX", station.posX.toFloat()) - putFloat("$bookmarkKey-posY", station.posY.toFloat()) - putMutableType( - this, - "$bookmarkKey-stationId", - station.stationId - ) - putString("$bookmarkKey-displayId", displayId) - putStringSet("bookmark-list", newBookmark) - commit() - } - } - } - StationInfoSelection.REFRESH -> { - scope.launch { - try { - busList = getRoute(Dispatchers.Default, lastStation) - } catch (_: SocketTimeoutException) {} - } - } - } + ComposeStationInfoPage(station = lastStation!!, scope = scope) { errorMessage: CharSequence -> + ConfirmationOverlay() + .setType(ConfirmationOverlay.FAILURE_ANIMATION) + .setMessage(errorMessage) + .showOn(activity) + navigationController.popBackStack() + return@ComposeStationInfoPage } } } @@ -343,6 +249,7 @@ class ComposeApp(private val activity: MainActivity): BaseCompose(activity.clien StationListPage(title, stationList, location, scope, isLoading, true, onSuccess) } + private suspend fun getStationList( stationType: StationListType, query: String, diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt index b9d8d32..2383e0d 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt @@ -7,12 +7,10 @@ import androidx.core.content.edit import androidx.wear.tiles.TileService import androidx.wear.widget.ConfirmationOverlay import com.google.accompanist.pager.rememberPagerState -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kr.yhs.traffic.R import kr.yhs.traffic.SettingTileActivity -import kr.yhs.traffic.TileType +import kr.yhs.traffic.StationTileType import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute import kr.yhs.traffic.ui.components.AccompanistPager @@ -25,12 +23,10 @@ import java.net.SocketTimeoutException class ComposeSettingTile( private val activity: SettingTileActivity, - private val tileType: TileType + private val stationTileType: StationTileType ) : BaseCompose(activity.client) { - private val defaultDispatcher: CoroutineDispatcher = Dispatchers.IO - override fun getPreferences(filename: String): SharedPreferences = - activity.getPreferences(filename) + override fun getPreferences(filename: String): SharedPreferences = activity.getPreferences(filename) @Composable override fun Content() { @@ -38,14 +34,15 @@ class ComposeSettingTile( val pagerState = rememberPagerState() var station by remember { mutableStateOf(null) } var route by remember { mutableStateOf>(listOf()) } - val preferences = getPreferences(tileType.preferenceId) + val preferences = getPreferences(stationTileType.preferenceId) AccompanistPager( scope = coroutineScope, pagerState = pagerState, pages = listOf({ StepPage( - title = tileType.title, - description = "${tileType.title}에 불러올 등록할 정류장를 선택해주세요. 즐겨찾기에 등록되어 있어야합니다." + this@ComposeSettingTile.activity, + title = stationTileType.title, + description = "${stationTileType.title}에 불러올 등록할 정류장를 선택해주세요. 즐겨찾기에 등록되어 있어야합니다.", enableStopButton = true ) { coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) } } @@ -72,7 +69,7 @@ class ComposeSettingTile( mutableStateOf(listOf()) } var isLoaded by remember { mutableStateOf(false) } - Log.i("stationInfo", station.toString()) + // Log.i("stationInfo", station.toString()) if (station != null) { LaunchedEffect(true) { try { @@ -103,7 +100,7 @@ class ComposeSettingTile( coroutineScope, isLoaded, pagerState.currentPage == 2, - tileType.maxBusSelect + stationTileType.maxBusSelect ) { stationRoute -> coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) @@ -144,11 +141,13 @@ class ComposeSettingTile( } } StepPage( - title = tileType.title, - description = "성공적으로 ${tileType.title}에 ${station?.name}(${station?.displayId})을 등록하였습니다.", - "완료" + this@ComposeSettingTile.activity, + title = stationTileType.title, + description = "성공적으로 ${stationTileType.title}에 ${station?.name}(${station?.displayId})을 등록하였습니다.", + "완료", false ) { - TileService.getUpdater(activity.baseContext).requestUpdate(tileType.classJava) + Log.i("TileService", "${stationTileType.preferenceId} ${stationTileType.classJava}") + TileService.getUpdater(activity.baseContext).requestUpdate(stationTileType.classJava) activity.finish() } }), diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeStationInfo.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeStationInfo.kt new file mode 100644 index 0000000..e8d7926 --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeStationInfo.kt @@ -0,0 +1,26 @@ +package kr.yhs.traffic.ui + +import android.content.SharedPreferences +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import kr.yhs.traffic.StationInfoActivity +import kr.yhs.traffic.TileType +import kr.yhs.traffic.utils.StationPreferences + + +class ComposeStationInfo( + private val activity: StationInfoActivity, + private val tileType: TileType +) : BaseComposeStationInfo(activity, activity.client), StationPreferences { + override fun getPreferences(filename: String): SharedPreferences = activity.getPreferences(filename) + + @Composable + override fun Content() { + val coroutineScope = rememberCoroutineScope() + val preferences = getPreferences(this.tileType.preferenceId) + val stationInfo = getStationInfo(preferences) + ComposeStationInfoPage(station = stationInfo, scope = coroutineScope) { + activity.finish() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt index 05b9a77..ca210c4 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/RouteSelection.kt @@ -107,17 +107,28 @@ class RouteSelection(private val context: Activity) { } } item { - NextButton( - modifier = Modifier.padding(top = 30.dp) - ) { - if (checkedRoute.size < maxSelect) { - ConfirmationOverlay() - .setType(ConfirmationOverlay.FAILURE_ANIMATION) - .setMessage("${maxSelect}개의 버스 노선을 선택해 주세요.") - .showOn(context) - return@NextButton + Row (modifier = Modifier.padding(top = 30.dp)) { + NextButton( + modifier = Modifier + .width(60.dp) + .align(Alignment.CenterVertically) + .padding(end = 3.dp), + "취소", + ) { context.finish() } + NextButton( + modifier = Modifier + .width(60.dp) + .padding(start = 3.dp) + ) { + if (checkedRoute.size < maxSelect) { + ConfirmationOverlay() + .setType(ConfirmationOverlay.FAILURE_ANIMATION) + .setMessage("${maxSelect}개의 버스 노선을 선택해 주세요.") + .showOn(context) + return@NextButton + } + callback.invoke(checkedRoute) } - callback.invoke(checkedRoute) } } } diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/StepPage.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/StepPage.kt index cca888a..909effe 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/StepPage.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/StepPage.kt @@ -1,9 +1,7 @@ package kr.yhs.traffic.ui.components -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding +import android.app.Activity +import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -12,13 +10,14 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.wear.compose.material.Text -import java.time.format.TextStyle @Composable fun StepPage( + activity: Activity, title: String, description: String, buttonText: String = "다음", + enableStopButton: Boolean = false, nextButtonCallback: () -> Unit ) { Column( @@ -42,10 +41,33 @@ fun StepPage( fontWeight = FontWeight.Medium, textAlign = TextAlign.Center ) - NextButton( - modifier = Modifier.align(Alignment.CenterHorizontally), - buttonText, - nextButtonCallback - ) + when (enableStopButton) { + true -> Row( + modifier = Modifier.align(Alignment.CenterHorizontally) + ) { + NextButton( + modifier = Modifier + .width(60.dp) + .align(Alignment.CenterVertically) + .padding(end = 3.dp), + "취소", + ) { activity.finish() } + NextButton( + modifier = Modifier + .width(60.dp) + .align(Alignment.CenterVertically) + .padding(start = 3.dp), + buttonText, + nextButtonCallback + ) + } + else -> NextButton( + modifier = Modifier + .width(100.dp) + .align(Alignment.CenterHorizontally), + buttonText, + nextButtonCallback + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationGPS.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationGPS.kt similarity index 97% rename from app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationGPS.kt rename to app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationGPS.kt index 1441eab..832d797 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationGPS.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationGPS.kt @@ -1,4 +1,4 @@ -package kr.yhs.traffic.ui.pages.navigator +package kr.yhs.traffic.ui.pages.pager import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationSearch.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationSearch.kt similarity index 98% rename from app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationSearch.kt rename to app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationSearch.kt index beea537..d7550b9 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationSearch.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationSearch.kt @@ -1,4 +1,4 @@ -package kr.yhs.traffic.ui.pages.navigator +package kr.yhs.traffic.ui.pages.pager import androidx.compose.foundation.background import androidx.compose.foundation.clickable diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationStar.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationStar.kt similarity index 97% rename from app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationStar.kt rename to app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationStar.kt index d8ab473..f5d702e 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/navigator/StationStar.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/pager/StationStar.kt @@ -1,4 +1,4 @@ -package kr.yhs.traffic.ui.pages.navigator +package kr.yhs.traffic.ui.pages.pager import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable diff --git a/app/src/main/java/kr/yhs/traffic/utils/StationPreferences.kt b/app/src/main/java/kr/yhs/traffic/utils/StationPreferences.kt new file mode 100644 index 0000000..ab82f4d --- /dev/null +++ b/app/src/main/java/kr/yhs/traffic/utils/StationPreferences.kt @@ -0,0 +1,17 @@ +package kr.yhs.traffic.utils + +import android.content.SharedPreferences +import kr.yhs.traffic.models.StationInfo + +interface StationPreferences: MutableTypeSharedPreferences { + fun getStationInfo(preference: SharedPreferences) = StationInfo( + preference.getString("station-name", "알 수 없음") ?: "알 수 없음", + preference.getString("station-id", null) ?: "-2", + preference.getString("station-ids", null), + preference.getFloat("station-posX", 0.0F).toDouble(), + preference.getFloat("station-posY", 0.0F).toDouble(), + preference.getString("station-displayId", null) ?: "0", + getMutableType(preference, "station-stationId", null) ?: "0", + preference.getInt("station-type", 0), + ) +} \ No newline at end of file From d6707d6c2e893edd49b8e4da87b76fa8ca8bec12 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Fri, 16 Dec 2022 16:27:54 +0900 Subject: [PATCH 21/23] Code Clean --- app/build.gradle | 6 ++--- .../tiles/services/BaseStationTileService.kt | 8 +++---- .../tiles/services/Station1TileService.kt | 24 ++++++++++++------- .../tiles/services/Station2TileService.kt | 8 +++++-- .../main/java/kr/yhs/traffic/ui/ComposeApp.kt | 2 +- .../kr/yhs/traffic/ui/ComposeSettingTile.kt | 14 +++++++---- app/src/main/res/values-en/strings.xml | 17 +++++++++---- app/src/main/res/values/strings.xml | 13 ++++++++-- gradle/libs.versions.toml | 2 ++ 9 files changed, 65 insertions(+), 29 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c941969..86d0ee4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "kr.yhs.traffic" minSdk 26 targetSdk 33 - versionCode 1112 - versionName "1.1.2" + versionCode 1902 + versionName "1.2.0-beta01" } buildTypes { release { @@ -40,6 +40,7 @@ android { dependencies { implementation(libs.androidx.coreKtx) + implementation(libs.kotlin.reflect) implementation(libs.percentlayout) implementation(libs.legacy) implementation(libs.lifecycle) @@ -88,7 +89,6 @@ dependencies { // Fragment implementation(libs.androidx.fragment) implementation(libs.androidx.fragmentKtx) - implementation ("org.jetbrains.kotlin:kotlin-reflect:1.7.20") // For Testing testImplementation(libs.bundles.tests) diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt index d7e9d2e..97e1d40 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/BaseStationTileService.kt @@ -83,7 +83,7 @@ abstract class BaseStationTileService( if (!preferences.contains("station")) { addContent ( SettingRequirement(this@BaseStationTileService.baseContext).content( - "실시간 버스 정보", "도착할 버스 정보를 불러오기 위한 버스 정류장을 등록해주세요.", + getString(R.string.station_tile_service_title), getString(R.string.station_tile_service_description), clickable(SettingTileActivity::class.java.name, this@BaseStationTileService) ) ) @@ -93,7 +93,7 @@ abstract class BaseStationTileService( val defaultBusRouteInfo = busRouteId?.map { StationRoute( it, - preferences.getString("$it-name", null) ?: "알 수 없음", + preferences.getString("$it-name", null) ?: getString(R.string.arrival_text_unknown), preferences.getInt("$it-type", 0), isEnd = false, isWait = false, arrivalInfo = listOf() ) @@ -148,8 +148,8 @@ abstract class BaseStationTileService( fun getArrivalText(routeInfo: StationRoute): String { return when { - routeInfo.isEnd == true -> "운행 종료" - routeInfo.isWait == true -> "운행 대기" + routeInfo.isEnd == true -> this.getString(R.string.arrival_text_closed) + routeInfo.isWait == true -> this.getString(R.string.arrival_text_wait) routeInfo.arrivalInfo.isNotEmpty() -> { val arrivalInfo = routeInfo.arrivalInfo[0] if (arrivalInfo.prevCount == 0 && arrivalInfo.time <= 180 || arrivalInfo.time <= 60) diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt index 1a8442e..1d57058 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station1TileService.kt @@ -1,25 +1,27 @@ package kr.yhs.traffic.tiles.services -import androidx.wear.tiles.* +import androidx.wear.tiles.DeviceParametersBuilders import androidx.wear.tiles.DimensionBuilders.dp import androidx.wear.tiles.DimensionBuilders.sp -import androidx.wear.tiles.LayoutElementBuilders.Spacer -import kr.yhs.traffic.SettingTileActivity -import kr.yhs.traffic.StationInfoActivity +import androidx.wear.tiles.LayoutElementBuilders +import kr.yhs.traffic.R import kr.yhs.traffic.models.StationInfo import kr.yhs.traffic.models.StationRoute -import kr.yhs.traffic.tiles.components.* +import kr.yhs.traffic.tiles.components.busArrivalText +import kr.yhs.traffic.tiles.components.busRouteText +import kr.yhs.traffic.tiles.components.spacer +import kr.yhs.traffic.tiles.components.stationText import kr.yhs.traffic.tiles.textButton class Station1TileService : BaseStationTileService("Station1Tile", "1") { override suspend fun stationTileLayout( deviceParameters: DeviceParametersBuilders.DeviceParameters, stationInfo: StationInfo, - busRoute: List? + routeInfo: List? ): LayoutElementBuilders.LayoutElement = LayoutElementBuilders.Column.Builder() .apply { - addContent(busRouteText(busRoute!![0], sp(19f), dp(6f))) + addContent(busRouteText(routeInfo!![0], sp(19f), dp(6f))) addContent( spacer(height = dp(2f)) ) @@ -28,13 +30,17 @@ class Station1TileService : BaseStationTileService("Station1Tile", "1") { spacer(height = dp(30f)) ) addContent( - busArrivalText(this@Station1TileService.getArrivalText(busRoute[0])) + busArrivalText(this@Station1TileService.getArrivalText(routeInfo[0])) ) addContent( spacer(height = dp(30f)) ) addContent( - textButton(this@Station1TileService.baseContext, "조회하기", updateClickable) + textButton( + this@Station1TileService.baseContext, + getString(R.string.station_tile_service_update_button), + updateClickable + ) ) }.build() } diff --git a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt index fc06b27..df39938 100644 --- a/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt +++ b/app/src/main/java/kr/yhs/traffic/tiles/services/Station2TileService.kt @@ -1,6 +1,5 @@ package kr.yhs.traffic.tiles.services -import android.util.Log import androidx.wear.tiles.* import androidx.wear.tiles.DimensionBuilders.* import androidx.wear.tiles.LayoutElementBuilders.VERTICAL_ALIGN_CENTER @@ -12,6 +11,7 @@ import kr.yhs.traffic.tiles.components.busRouteText import kr.yhs.traffic.tiles.components.spacer import kr.yhs.traffic.tiles.components.stationText import kr.yhs.traffic.tiles.textButton +import kr.yhs.traffic.R class Station2TileService : BaseStationTileService("Station2Tile", "1") { override suspend fun stationTileLayout( @@ -32,7 +32,11 @@ class Station2TileService : BaseStationTileService("Station2Tile", "1") { spacer(height = dp(30f)) ) addContent( - textButton(this@Station2TileService.baseContext, "조회하기", updateClickable) + textButton( + this@Station2TileService.baseContext, + getString(R.string.station_tile_service_update_button), + updateClickable + ) ) }.build() diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt index 6faffd9..3b18862 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeApp.kt @@ -228,7 +228,7 @@ class ComposeApp(private val activity: MainActivity): BaseComposeStationInfo(act stationType = stationType, query = query, cityCode = cityCode, - location = location!! + location = location ) isLoading = false } catch (e: SocketTimeoutException) { diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt index 2383e0d..c27ac55 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeSettingTile.kt @@ -42,14 +42,20 @@ class ComposeSettingTile( StepPage( this@ComposeSettingTile.activity, title = stationTileType.title, - description = "${stationTileType.title}에 불러올 등록할 정류장를 선택해주세요. 즐겨찾기에 등록되어 있어야합니다.", enableStopButton = true + description = activity.getString(R.string.station_tile_setting_first_description, stationTileType.title), + enableStopButton = true ) { coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) } } }, { val bookmarkStation = getStationBookmarkList() StationListPage( - "등록할 정류장", bookmarkStation, null, coroutineScope, false, pagerState.currentPage == 1 + activity.getString(R.string.station_tile_setting_station_list_title), + bookmarkStation, + null, + coroutineScope, + false, + pagerState.currentPage == 1 ) { station = it coroutineScope.launch { @@ -143,8 +149,8 @@ class ComposeSettingTile( StepPage( this@ComposeSettingTile.activity, title = stationTileType.title, - description = "성공적으로 ${stationTileType.title}에 ${station?.name}(${station?.displayId})을 등록하였습니다.", - "완료", false + description = activity.getString(R.string.station_tile_setting_success_description, stationTileType.title, station?.name, station?.displayId), + activity.getString(R.string.station_tile_setting_success_button), false ) { Log.i("TileService", "${stationTileType.preferenceId} ${stationTileType.classJava}") TileService.getUpdater(activity.baseContext).requestUpdate(stationTileType.classJava) diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index fbded1d..c23c69b 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -43,9 +43,18 @@ Wait for Departure Unknown - 버스정류장 곧 도착 버스 정보 - 곧 도착하는 버스 목록을 불러옵니다. + Arrival Info(1) + Load bus arrival info from bus station. - 버스정류장 곧 도착 버스 정보 - 곧 도착하는 버스 목록을 불러옵니다. + Arrival Info(2) + Load bus arrival info from bus station. + + Arrival Info + Please add bus station to get bus arrival information. + Update + + Please select station to register for %s. You must be registered as a favorite. + Registered Station + Successfully registered %s to %s(%s) + Success \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 84456bf..2102bbf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -44,9 +44,18 @@ 출발 대기 정보 없음 - 버스정류장 실시간 도착정보(1개) + 실시간 도착정보(1개) 해당하는 버스 정류장의 도착정보를 불러옵니다. - 버스정류장 실시간 도착정보(2개) + 실시간 도착정보(2개) 해당하는 버스 정류장의 도착정보를 불러옵니다. + + 실시간 도착정보 + 도착할 버스 정보를 불러오기 위한 버스 정류장을 등록해주세요. + 조회하기 + + 성공적으로 %s에 %s(%s)을 등록하였습니다. + %s에 불러올 등록할 정류장를 선택해주세요. 즐겨찾기에 등록되어 있어야합니다. + 등록할 정류장 + 완료 \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 333ec7b..57313cd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,6 +37,8 @@ compose-wear-navigation = { module = "androidx.wear.compose:compose-navigation", androidx-coreKtx = "androidx.core:core-ktx:1.9.0" androidx-activity-compose = "androidx.activity:activity-compose:1.6.0" +kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } + google-gms-wearable = "com.google.android.gms:play-services-wearable:18.0.0" google-gms-location = "com.google.android.gms:play-services-location:21.0.0" From f2003f58e8260d5642dc2b212ad97081e6414943 Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Fri, 30 Dec 2022 23:19:50 +0900 Subject: [PATCH 22/23] Add Exit Button --- .../main/java/kr/yhs/traffic/MainActivity.kt | 5 +- .../kr/yhs/traffic/StationInfoActivity.kt | 8 ++ .../yhs/traffic/ui/BaseComposeStationInfo.kt | 13 ++- .../kr/yhs/traffic/ui/ComposeStationInfo.kt | 15 +++- .../yhs/traffic/ui/pages/StationInfoPage.kt | 84 ++++++++++++------- .../traffic/ui/theme/StationInfoSelection.kt | 3 +- .../main/res/drawable/ic_baseline_exit.xml | 5 ++ 7 files changed, 94 insertions(+), 39 deletions(-) create mode 100644 app/src/main/res/drawable/ic_baseline_exit.xml diff --git a/app/src/main/java/kr/yhs/traffic/MainActivity.kt b/app/src/main/java/kr/yhs/traffic/MainActivity.kt index eeffe9e..83496f6 100644 --- a/app/src/main/java/kr/yhs/traffic/MainActivity.kt +++ b/app/src/main/java/kr/yhs/traffic/MainActivity.kt @@ -7,12 +7,9 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices -import kr.yhs.traffic.utils.TrafficClient import kr.yhs.traffic.ui.ComposeApp import kr.yhs.traffic.utils.ClientBuilder -import okhttp3.OkHttpClient -import retrofit2.Retrofit -import retrofit2.converter.gson.GsonConverterFactory +import kr.yhs.traffic.utils.TrafficClient class MainActivity : ComponentActivity() { var fusedLocationClient: FusedLocationProviderClient? = null diff --git a/app/src/main/java/kr/yhs/traffic/StationInfoActivity.kt b/app/src/main/java/kr/yhs/traffic/StationInfoActivity.kt index 60ce27b..8fdc9b2 100644 --- a/app/src/main/java/kr/yhs/traffic/StationInfoActivity.kt +++ b/app/src/main/java/kr/yhs/traffic/StationInfoActivity.kt @@ -1,6 +1,7 @@ package kr.yhs.traffic import android.os.Bundle +import androidx.activity.OnBackPressedCallback import androidx.activity.compose.setContent import androidx.fragment.app.FragmentActivity import androidx.wear.tiles.TileService @@ -29,6 +30,13 @@ class StationInfoActivity: FragmentActivity() { val retrofit = clientBuilder.build() client = retrofit.create(TrafficClient::class.java) + val onBackPressedCallback = object: OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + this@StationInfoActivity.finish() + } + } + this.onBackPressedDispatcher.addCallback(onBackPressedCallback) + setContent { ComposeStationInfo(this, stationTileType!!).Content() } diff --git a/app/src/main/java/kr/yhs/traffic/ui/BaseComposeStationInfo.kt b/app/src/main/java/kr/yhs/traffic/ui/BaseComposeStationInfo.kt index 44c6401..e6ac4e5 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/BaseComposeStationInfo.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/BaseComposeStationInfo.kt @@ -24,6 +24,7 @@ abstract class BaseComposeStationInfo( fun ComposeStationInfoPage( station: StationInfo, scope: CoroutineScope, + isTile: Boolean = false, onFailed: (CharSequence) -> Unit ) { var busList by remember { mutableStateOf>(emptyList()) } @@ -52,8 +53,13 @@ abstract class BaseComposeStationInfo( val bookmarkKey = "${station.routeId}0${station.type}" StationInfoPage( - station, busList, - bookmark.contains(bookmarkKey), isLoading, scope + stationInfo = station, + busInfo = busList, + starActive = bookmark.contains(bookmarkKey), + isLoading = isLoading, + scope = scope, + buttonList = if (isTile) listOf(StationInfoSelection.EXIT, StationInfoSelection.REFRESH) + else listOf(StationInfoSelection.BOOKMARK, StationInfoSelection.REFRESH) ) { when (it) { StationInfoSelection.BOOKMARK -> { @@ -105,6 +111,9 @@ abstract class BaseComposeStationInfo( } catch (_: SocketTimeoutException) {} } } + StationInfoSelection.EXIT -> { + activity.finish() + } } } } diff --git a/app/src/main/java/kr/yhs/traffic/ui/ComposeStationInfo.kt b/app/src/main/java/kr/yhs/traffic/ui/ComposeStationInfo.kt index e8d7926..e4e2939 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/ComposeStationInfo.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/ComposeStationInfo.kt @@ -1,10 +1,23 @@ package kr.yhs.traffic.ui import android.content.SharedPreferences +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material.Button +import androidx.wear.compose.material.ButtonDefaults +import androidx.wear.compose.material.Icon +import androidx.wear.compose.material.LocalContentColor +import kr.yhs.traffic.R import kr.yhs.traffic.StationInfoActivity import kr.yhs.traffic.TileType +import kr.yhs.traffic.ui.components.NextButton +import kr.yhs.traffic.ui.theme.StationInfoSelection import kr.yhs.traffic.utils.StationPreferences @@ -19,7 +32,7 @@ class ComposeStationInfo( val coroutineScope = rememberCoroutineScope() val preferences = getPreferences(this.tileType.preferenceId) val stationInfo = getStationInfo(preferences) - ComposeStationInfoPage(station = stationInfo, scope = coroutineScope) { + ComposeStationInfoPage(station = stationInfo, scope = coroutineScope, isTile = true) { activity.finish() } } diff --git a/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt b/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt index 5c66895..e09a541 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/pages/StationInfoPage.kt @@ -38,6 +38,7 @@ fun StationInfoPage( starActive: Boolean = false, isLoading: Boolean = false, scope: CoroutineScope, + buttonList: List = listOf(StationInfoSelection.REFRESH, StationInfoSelection.BOOKMARK), callback: (StationInfoSelection) -> Unit ) { val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState() @@ -87,41 +88,62 @@ fun StationInfoPage( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceEvenly ) { - Button( - modifier = Modifier.size( - width = ButtonDefaults.LargeButtonSize, - height = ButtonDefaults.ExtraSmallButtonSize - ), - onClick = { - bookmarkActive = !bookmarkActive - callback(StationInfoSelection.BOOKMARK) + if (buttonList.contains(StationInfoSelection.BOOKMARK)) { + Button( + modifier = Modifier.size( + width = ButtonDefaults.LargeButtonSize, + height = ButtonDefaults.ExtraSmallButtonSize + ), + onClick = { + bookmarkActive = !bookmarkActive + callback(StationInfoSelection.BOOKMARK) + } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_star), + contentDescription = "star", + modifier = Modifier.size(16.dp), + tint = when (bookmarkActive) { + true -> Color.Yellow + false -> LocalContentColor.current + } + ) } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_baseline_star), - contentDescription = "star", - modifier = Modifier.size(16.dp), - tint = when(bookmarkActive) { - true -> Color.Yellow - false -> LocalContentColor.current + } + if (buttonList.contains(StationInfoSelection.REFRESH)) { + Button( + modifier = Modifier.size( + width = ButtonDefaults.LargeButtonSize, + height = ButtonDefaults.ExtraSmallButtonSize + ), + enabled = autoUpdate, + onClick = { + callback(StationInfoSelection.REFRESH) } - ) + ) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_refresh), + contentDescription = "refresh", + modifier = Modifier.size(16.dp), + ) + } } - Button( - modifier = Modifier.size( - width = ButtonDefaults.LargeButtonSize, - height = ButtonDefaults.ExtraSmallButtonSize - ), - enabled = autoUpdate, - onClick = { - callback(StationInfoSelection.REFRESH) + if (buttonList.contains(StationInfoSelection.EXIT)) { + Button( + modifier = Modifier.size( + width = ButtonDefaults.LargeButtonSize, + height = ButtonDefaults.ExtraSmallButtonSize + ), + onClick = { + callback(StationInfoSelection.EXIT) + } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_baseline_exit), + contentDescription = "refresh", + modifier = Modifier.size(16.dp), + ) } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_baseline_refresh), - contentDescription = "refresh", - modifier = Modifier.size(16.dp), - ) } } } diff --git a/app/src/main/java/kr/yhs/traffic/ui/theme/StationInfoSelection.kt b/app/src/main/java/kr/yhs/traffic/ui/theme/StationInfoSelection.kt index 1434fc9..d3298d6 100644 --- a/app/src/main/java/kr/yhs/traffic/ui/theme/StationInfoSelection.kt +++ b/app/src/main/java/kr/yhs/traffic/ui/theme/StationInfoSelection.kt @@ -2,5 +2,6 @@ package kr.yhs.traffic.ui.theme enum class StationInfoSelection { BOOKMARK, - REFRESH + REFRESH, + EXIT } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_baseline_exit.xml b/app/src/main/res/drawable/ic_baseline_exit.xml new file mode 100644 index 0000000..63f05ce --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_exit.xml @@ -0,0 +1,5 @@ + + + From 424eac4f922f1499a7c7520ae29f62904f12d9fa Mon Sep 17 00:00:00 2001 From: gunyu1019 Date: Fri, 30 Dec 2022 23:36:10 +0900 Subject: [PATCH 23/23] 1.2.0 released! --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 86d0ee4..0b728be 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId "kr.yhs.traffic" minSdk 26 targetSdk 33 - versionCode 1902 - versionName "1.2.0-beta01" + versionCode 1112 + versionName "1.2.0" } buildTypes { release {