diff --git a/apps/giga-wear/client/build.gradle.kts b/apps/giga-wear/client/build.gradle.kts index 77705e6..911969d 100644 --- a/apps/giga-wear/client/build.gradle.kts +++ b/apps/giga-wear/client/build.gradle.kts @@ -5,9 +5,10 @@ plugins { alias(libs.plugins.convention.compose) alias(libs.plugins.convention.koin) alias(libs.plugins.convention.ktor.client) + alias(libs.plugins.convention.serialization) } -val appVersion = "1.27.1" +val appVersion = "1.0" android { buildFeatures { buildConfig = true } @@ -24,6 +25,9 @@ android { dependencies { implementation(platform(libs.androidx.compose.bom)) + implementation(project(":libs:ui")) + implementation(project(":libs:http-client")) + implementation(project(":libs:base")) implementation(libs.activity.compose) implementation(libs.androidx.compose.foundation) implementation(libs.androidx.compose.material3) diff --git a/apps/giga-wear/client/src/main/AndroidManifest.xml b/apps/giga-wear/client/src/main/AndroidManifest.xml index 0943432..2d1e0f3 100644 --- a/apps/giga-wear/client/src/main/AndroidManifest.xml +++ b/apps/giga-wear/client/src/main/AndroidManifest.xml @@ -6,51 +6,21 @@ - - - - - - - - - - - - - - - + android:required="false" /> + - + @@ -68,4 +38,4 @@ - \ No newline at end of file + diff --git a/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/complication/MainComplicationService.kt b/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/complication/MainComplicationService.kt deleted file mode 100644 index 39ebf81..0000000 --- a/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/complication/MainComplicationService.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ru.shadowsparky.gigawear.complication - -import androidx.wear.watchface.complications.data.ComplicationData -import androidx.wear.watchface.complications.data.ComplicationType -import androidx.wear.watchface.complications.data.PlainComplicationText -import androidx.wear.watchface.complications.data.ShortTextComplicationData -import androidx.wear.watchface.complications.datasource.ComplicationRequest -import androidx.wear.watchface.complications.datasource.SuspendingComplicationDataSourceService -import java.util.Calendar - -/** - * Skeleton for complication data source that returns short text. - */ -class MainComplicationService : SuspendingComplicationDataSourceService() { - - override fun getPreviewData(type: ComplicationType): ComplicationData? { - if (type != ComplicationType.SHORT_TEXT) { - return null - } - return createComplicationData("Mon", "Monday") - } - - override suspend fun onComplicationRequest(request: ComplicationRequest): ComplicationData { - return when (Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) { - Calendar.SUNDAY -> createComplicationData("Sun", "Sunday") - Calendar.MONDAY -> createComplicationData("Mon", "Monday") - Calendar.TUESDAY -> createComplicationData("Tue", "Tuesday") - Calendar.WEDNESDAY -> createComplicationData("Wed", "Wednesday") - Calendar.THURSDAY -> createComplicationData("Thu", "Thursday") - Calendar.FRIDAY -> createComplicationData("Fri", "Friday") - Calendar.SATURDAY -> createComplicationData("Sat", "Saturday") - else -> throw IllegalArgumentException("too many days") - } - } - - private fun createComplicationData(text: String, contentDescription: String) = - ShortTextComplicationData.Builder( - text = PlainComplicationText.Builder(text).build(), - contentDescription = PlainComplicationText.Builder(contentDescription).build() - ).build() -} \ No newline at end of file diff --git a/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/presentation/MainActivity.kt b/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/presentation/MainActivity.kt deleted file mode 100644 index 07f949c..0000000 --- a/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/presentation/MainActivity.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* While this template provides a good starting point for using Wear Compose, you can always - * take a look at https://github.com/android/wear-os-samples/tree/main/ComposeStarter to find the - * most up to date changes to the libraries and their usages. - */ - -package ru.shadowsparky.gigawear.presentation - -import android.app.Activity -import android.content.Intent -import android.os.Bundle -import android.speech.RecognizerIntent -import android.speech.tts.TextToSpeech -import androidx.activity.ComponentActivity -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.compose.setContent -import androidx.activity.result.contract.ActivityResultContracts -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.wear.compose.foundation.lazy.TransformingLazyColumn -import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState -import androidx.wear.compose.material3.AppScaffold -import androidx.wear.compose.material3.Button -import androidx.wear.compose.material3.ButtonDefaults -import androidx.wear.compose.material3.EdgeButton -import androidx.wear.compose.material3.ListHeader -import androidx.wear.compose.material3.MaterialTheme -import androidx.wear.compose.material3.ScreenScaffold -import androidx.wear.compose.material3.SurfaceTransformation -import androidx.wear.compose.material3.Text -import androidx.wear.compose.material3.lazy.rememberTransformationSpec -import androidx.wear.compose.material3.lazy.transformedHeight -import ru.shadowsparky.gigawear.R -import ru.shadowsparky.gigawear.presentation.theme.AndroidDevTheme -import java.util.Locale - -class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContent { - WearApp("Android") - } - } -} - -@Composable -fun WearApp(greetingName: String) { - val context = LocalContext.current - var spokenText by remember { mutableStateOf("Тут будет твой текст") } - val tts = remember { mutableStateOf(null) } - DisposableEffect(Unit) { - tts.value = TextToSpeech(context) { status -> - if (status == TextToSpeech.SUCCESS) { - tts.value?.language = Locale.getDefault() - } - } - onDispose { - tts.value?.stop() - tts.value?.shutdown() - } - } - val voiceLauncher = rememberLauncherForActivityResult( - contract = ActivityResultContracts.StartActivityForResult() - ) { result -> - if (result.resultCode == Activity.RESULT_OK) { - val results = result.data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS) - val text = results?.firstOrNull() - if (!text.isNullOrBlank()) { - spokenText = text - tts.value?.speak(text, TextToSpeech.QUEUE_FLUSH, null, null) - } - } - } - AndroidDevTheme { - AppScaffold { - val listState = rememberTransformingLazyColumnState() - val transformationSpec = rememberTransformationSpec() - ScreenScaffold( - scrollState = listState, - edgeButton = { - EdgeButton( - onClick = { - val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).apply { - putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM) - putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault().toString()) - } - try { - voiceLauncher.launch(intent) - } catch (e: Exception) { - spokenText = " Ошибка: Голосовой ввод недоступен" - } - }, - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.primaryContainer, - contentColor = MaterialTheme.colorScheme.onPrimaryContainer, - ), - ) { - Text(" Начать ввод") - } - }, - ) { contentPadding -> - TransformingLazyColumn(contentPadding = contentPadding, state = listState) { - item { - ListHeader( - modifier = Modifier.fillMaxWidth().transformedHeight(this, transformationSpec), - transformation = SurfaceTransformation(transformationSpec), - ) { - Text(text = stringResource(R.string.hello_world, greetingName)) - } - } - item { - Button( - onClick = { - tts.value?.speak(spokenText, TextToSpeech.QUEUE_FLUSH, null, null) - }, - modifier = Modifier.fillMaxWidth().transformedHeight(this, transformationSpec), - transformation = SurfaceTransformation(transformationSpec), - colors = ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.secondaryContainer - ) - ) { - Text(text = spokenText) - } - } - } - } - } - } -} diff --git a/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/presentation/theme/Theme.kt b/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/presentation/theme/Theme.kt deleted file mode 100644 index b5d29d3..0000000 --- a/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/presentation/theme/Theme.kt +++ /dev/null @@ -1,17 +0,0 @@ -package ru.shadowsparky.gigawear.presentation.theme - -import androidx.compose.runtime.Composable -import androidx.wear.compose.material3.MaterialTheme - -@Composable -fun AndroidDevTheme( - content: @Composable () -> Unit -) { - /** - * Empty theme to customize for your app. - * See: https://developer.android.com/jetpack/compose/designsystems/custom - */ - MaterialTheme( - content = content - ) -} \ No newline at end of file diff --git a/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/tile/MainTileService.kt b/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/tile/MainTileService.kt deleted file mode 100644 index 056eae4..0000000 --- a/apps/giga-wear/client/src/main/java/ru/shadowsparky/gigawear/tile/MainTileService.kt +++ /dev/null @@ -1,65 +0,0 @@ -package ru.shadowsparky.gigawear.tile - -import android.content.Context -import androidx.wear.protolayout.ResourceBuilders.Resources -import androidx.wear.protolayout.TimelineBuilders -import androidx.wear.protolayout.material3.Typography.BODY_LARGE -import androidx.wear.tiles.RequestBuilders -import androidx.wear.tiles.TileBuilders -import androidx.wear.tiles.TileService -import com.google.common.util.concurrent.Futures -import androidx.wear.protolayout.material3.materialScope -import androidx.wear.protolayout.material3.primaryLayout -import androidx.wear.protolayout.material3.text -import androidx.wear.protolayout.types.layoutString -import androidx.wear.tiles.RequestBuilders.ResourcesRequest -import androidx.wear.tiles.tooling.preview.Preview -import androidx.wear.tiles.tooling.preview.TilePreviewData -import androidx.wear.tooling.preview.devices.WearDevices -import ru.shadowsparky.gigawear.R -import com.google.common.util.concurrent.ListenableFuture - -private const val RESOURCES_VERSION = "0" - -class MainTileService : TileService() { - override fun onTileRequest(requestParams: RequestBuilders.TileRequest): ListenableFuture = - Futures.immediateFuture(tile(requestParams, this)) - - override fun onTileResourcesRequest(requestParams: ResourcesRequest): ListenableFuture = - Futures.immediateFuture(resources(requestParams)) -} - -private fun resources(requestParams: ResourcesRequest): Resources { - return Resources.Builder() - .setVersion(RESOURCES_VERSION) - .build() -} - -private fun tile( - requestParams: RequestBuilders.TileRequest, - context: Context, -): TileBuilders.Tile { - return TileBuilders.Tile.Builder() - .setResourcesVersion(RESOURCES_VERSION) - .setTileTimeline( - TimelineBuilders.Timeline.fromLayoutElement( - materialScope(context, requestParams.deviceConfiguration) { - primaryLayout( - mainSlot = { - text( - context.getString(R.string.hello_world, "Tile").layoutString, - typography = BODY_LARGE - ) - } - ) - } - ) - ) - .build() -} - -@Preview(device = WearDevices.SMALL_ROUND) -@Preview(device = WearDevices.LARGE_ROUND) -fun tilePreview(context: Context) = TilePreviewData(::resources) { - tile(it, context) -} \ No newline at end of file diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/GigaWearModule.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/GigaWearModule.kt new file mode 100644 index 0000000..e736c40 --- /dev/null +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/GigaWearModule.kt @@ -0,0 +1,10 @@ +package ru.shadowsparky.gigawear + +import org.koin.core.annotation.ComponentScan +import org.koin.core.annotation.Configuration +import org.koin.core.annotation.Module + +@Module +@ComponentScan +@Configuration +class GigaWearModule diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/App.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/App.kt new file mode 100644 index 0000000..4f2cd37 --- /dev/null +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/App.kt @@ -0,0 +1,16 @@ +package ru.shadowsparky.gigawear.presentation + +import android.app.Application +import org.koin.core.annotation.KoinApplication +import org.koin.plugin.module.dsl.startKoin + +@KoinApplication +annotation class GigaWearApp + +class App : Application() { + + override fun onCreate() { + super.onCreate() + startKoin() + } +} diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/GigaWearRootComponent.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/GigaWearRootComponent.kt new file mode 100644 index 0000000..acd0465 --- /dev/null +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/GigaWearRootComponent.kt @@ -0,0 +1,24 @@ +package ru.shadowsparky.gigawear.presentation + +import com.arkivanov.decompose.ComponentContext +import kotlinx.serialization.KSerializer +import org.koin.core.Koin +import org.koin.core.annotation.Factory +import ru.shadowsparky.gigawear.presentation.nav.Route +import ru.shadowsparky.ui.nav.RootComponent + +class GigaWearRootComponent( + context: ComponentContext +) : RootComponent(context) { + override val initStack: List = listOf(Route.MainScreen()) + override val serializer: KSerializer = Route.serializer() + override val koin: Koin = ru.shadowsparky.koin +} + +@Factory +class RootComponentFactory { + + fun create(context: ComponentContext): GigaWearRootComponent { + return GigaWearRootComponent(context) + } +} diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/MainActivity.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/MainActivity.kt new file mode 100644 index 0000000..0a5ac91 --- /dev/null +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/MainActivity.kt @@ -0,0 +1,26 @@ +package ru.shadowsparky.gigawear.presentation + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import com.arkivanov.decompose.retainedComponent +import org.koin.android.ext.android.inject +import ru.shadowsparky.gigawear.presentation.main.MainScreen +import ru.shadowsparky.gigawear.presentation.nav.Child +import ru.shadowsparky.ui.nav.Nav + +class MainActivity : ComponentActivity() { + private val rootFactory by inject() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val root = retainedComponent { rootFactory.create(it) } + setContent { + Nav(root) { child, _ -> + when (child) { + is Child.MainChild -> MainScreen(child.component) + } + } + } + } +} diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/main/MainComponent.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/main/MainComponent.kt new file mode 100644 index 0000000..d295ae6 --- /dev/null +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/main/MainComponent.kt @@ -0,0 +1,27 @@ +package ru.shadowsparky.gigawear.presentation.main + +import com.arkivanov.decompose.ComponentContext +import org.koin.core.annotation.Factory +import org.koin.core.annotation.Named +import ru.shadowsparky.gigawear.presentation.nav.Child +import ru.shadowsparky.gigawear.presentation.nav.Route +import ru.shadowsparky.ui.nav.ComponentFactory +import ru.shadowsparky.ui.nav.RouteNavigator + +class MainComponent( + private val context: ComponentContext +) : ComponentContext by context { +} + +@Factory +@Named(Route.MAIN) +class MainComponentFactory : ComponentFactory { + override fun create( + route: Route.MainScreen, + child: ComponentContext, + root: RouteNavigator + ): Child { + val component = MainComponent(child) + return Child.MainChild(component) + } +} diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/main/MainScreen.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/main/MainScreen.kt new file mode 100644 index 0000000..55f9039 --- /dev/null +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/main/MainScreen.kt @@ -0,0 +1,14 @@ +package ru.shadowsparky.gigawear.presentation.main + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.safeContentPadding +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +fun MainScreen(component: MainComponent) { + Box(modifier = Modifier.safeContentPadding()) { + Text("MainScreen $component") + } +} diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/nav/Child.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/nav/Child.kt new file mode 100644 index 0000000..7a8132b --- /dev/null +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/nav/Child.kt @@ -0,0 +1,7 @@ +package ru.shadowsparky.gigawear.presentation.nav + +import ru.shadowsparky.gigawear.presentation.main.MainComponent + +sealed class Child : ru.shadowsparky.ui.nav.Child { + class MainChild(val component: MainComponent) : Child() +} diff --git a/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/nav/Route.kt b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/nav/Route.kt new file mode 100644 index 0000000..19c0f3e --- /dev/null +++ b/apps/giga-wear/client/src/main/kotlin/ru/shadowsparky/gigawear/presentation/nav/Route.kt @@ -0,0 +1,14 @@ +package ru.shadowsparky.gigawear.presentation.nav + +import kotlinx.serialization.Serializable + +@Serializable +sealed class Route : ru.shadowsparky.ui.nav.Route() { + + @Serializable + data class MainScreen(override val routeId: String = MAIN) : Route() + + companion object { + const val MAIN = "main" + } +} diff --git a/apps/giga-wear/client/src/main/res/values/styles.xml b/apps/giga-wear/client/src/main/res/values/styles.xml index dbf9c83..a81c145 100644 --- a/apps/giga-wear/client/src/main/res/values/styles.xml +++ b/apps/giga-wear/client/src/main/res/values/styles.xml @@ -1,8 +1,7 @@ - - \ No newline at end of file +