Initial commit
							
								
								
									
										2
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | ||||
| /build | ||||
| /.idea | ||||
							
								
								
									
										81
									
								
								app/build.gradle.kts
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,81 @@ | ||||
| plugins { | ||||
|     id("com.android.application") | ||||
|     id("org.jetbrains.kotlin.android") | ||||
|     id("com.google.devtools.ksp") | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Не изменяйте ничего при сборки. Все изменения будут проигнорированы | ||||
|  **/ | ||||
| android { | ||||
|     namespace = "ru.samsung.smartintercom" | ||||
|     compileSdk = 34 | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId = "ru.samsung.smartintercom" | ||||
|         minSdk = 24 | ||||
|         targetSdk = 33 | ||||
|         versionCode = 1 | ||||
|         versionName = "1.0" | ||||
|  | ||||
|         testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" | ||||
|         vectorDrawables { | ||||
|             useSupportLibrary = true | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|         release { | ||||
|             isMinifyEnabled = false | ||||
|             proguardFiles( | ||||
|                 getDefaultProguardFile("proguard-android-optimize.txt"), | ||||
|                 "proguard-rules.pro" | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|     compileOptions { | ||||
|         sourceCompatibility = JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility = JavaVersion.VERSION_1_8 | ||||
|     } | ||||
|     kotlinOptions { | ||||
|         jvmTarget = "1.8" | ||||
|     } | ||||
|     buildFeatures { | ||||
|         compose = true | ||||
|     } | ||||
|     composeOptions { | ||||
|         kotlinCompilerExtensionVersion = "1.5.2" | ||||
|     } | ||||
|     packaging { | ||||
|         resources { | ||||
|             excludes += "/META-INF/{AL2.0,LGPL2.1}" | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Используйте только разрешённые библиотеки | ||||
|  **/ | ||||
| dependencies { | ||||
|     implementation("androidx.core:core-ktx:1.12.0") | ||||
|     implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") | ||||
|     implementation("androidx.activity:activity-compose:1.7.0") | ||||
|     implementation(platform("androidx.compose:compose-bom:2023.03.00")) | ||||
|     implementation("androidx.compose.ui:ui") | ||||
|     implementation("androidx.compose.ui:ui-graphics") | ||||
|     implementation("androidx.compose.ui:ui-tooling-preview") | ||||
|     implementation("androidx.compose.material3:material3") | ||||
|     implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2") | ||||
|     implementation("androidx.navigation:navigation-compose:2.7.4") | ||||
|  | ||||
|     implementation("com.squareup.retrofit2:retrofit:2.9.0") | ||||
|     implementation("com.squareup.retrofit2:converter-gson:2.9.0") | ||||
|  | ||||
|     implementation("com.google.dagger:dagger:2.48") | ||||
|     ksp("com.google.dagger:dagger-compiler:2.48") | ||||
|  | ||||
|     implementation("io.coil-kt:coil-compose:2.4.0") | ||||
|  | ||||
|     implementation("io.ktor:ktor-network:2.3.5") | ||||
| } | ||||
							
								
								
									
										21
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,21 @@ | ||||
| # Add project specific ProGuard rules here. | ||||
| # You can control the set of applied configuration files using the | ||||
| # proguardFiles setting in build.gradle. | ||||
| # | ||||
| # For more details, see | ||||
| #   http://developer.android.com/guide/developing/tools/proguard.html | ||||
|  | ||||
| # If your project uses WebView with JS, uncomment the following | ||||
| # and specify the fully qualified class name to the JavaScript interface | ||||
| # class: | ||||
| #-keepclassmembers class fqcn.of.javascript.interface.for.webview { | ||||
| #   public *; | ||||
| #} | ||||
|  | ||||
| # Uncomment this to preserve the line number information for | ||||
| # debugging stack traces. | ||||
| #-keepattributes SourceFile,LineNumberTable | ||||
|  | ||||
| # If you keep the line number information, uncomment this to | ||||
| # hide the original source file name. | ||||
| #-renamesourcefileattribute SourceFile | ||||
							
								
								
									
										35
									
								
								app/src/main/AndroidManifest.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,35 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|  | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|  | ||||
|     <application | ||||
|         android:name=".App" | ||||
|         android:allowBackup="true" | ||||
|         android:dataExtractionRules="@xml/data_extraction_rules" | ||||
|         android:fullBackupContent="@xml/backup_rules" | ||||
|         android:icon="@mipmap/ic_launcher" | ||||
|         android:label="@string/app_name" | ||||
|         android:roundIcon="@mipmap/ic_launcher_round" | ||||
|         android:supportsRtl="true" | ||||
|         android:theme="@style/Theme.SmartIntercom" | ||||
|         android:usesCleartextTraffic="true" | ||||
|         tools:targetApi="33"> | ||||
|         <!-- НЕ ИЗМЕНЯЙТЕ ПАКЕТ И АКТИВИТИ ДЛЯ ЗАПУСКА --> | ||||
|         <!-- ДОБАВЛЯТЬ НОВЫЕ - МОЖНО --> | ||||
|         <activity | ||||
|             android:name=".ui.activity.MainActivity" | ||||
|             android:exported="true" | ||||
|             android:label="@string/app_name" | ||||
|             android:theme="@style/Theme.SmartIntercom" | ||||
|             android:windowSoftInputMode="adjustResize"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MAIN" /> | ||||
|  | ||||
|                 <category android:name="android.intent.category.LAUNCHER" /> | ||||
|             </intent-filter> | ||||
|         </activity> | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
							
								
								
									
										11
									
								
								app/src/main/java/ru/samsung/smartintercom/App.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,11 @@ | ||||
| package ru.samsung.smartintercom | ||||
|  | ||||
| import android.app.Application | ||||
| import ru.samsung.smartintercom.di.ContextModule | ||||
| import ru.samsung.smartintercom.di.DaggerAppComponent | ||||
|  | ||||
| class App : Application() { | ||||
|     val appComponent = DaggerAppComponent.builder() | ||||
|         .contextModule(ContextModule(this)) | ||||
|         .build() | ||||
| } | ||||
| @@ -0,0 +1,37 @@ | ||||
| /* НЕ ИЗМЕНЯЙТЕ ФАЙЛ! */ | ||||
| /* ВСЕ ИЗМЕНЕНИЯ ПРИ ТЕСТИРОВАНИИ БУДУТ ОТМЕНЕНЫ */ | ||||
| package ru.samsung.smartintercom.core | ||||
|  | ||||
| typealias ComposeIds = String | ||||
|  | ||||
| object MainScreenId { | ||||
|     const val screenId: ComposeIds = "main_screen" | ||||
|     const val buttonStart: ComposeIds = "button_start" | ||||
|     const val buttonRetry: ComposeIds = "button_retry" | ||||
|     const val textIntercomModel: ComposeIds = "text_intercom_model" | ||||
|     const val buttonTakePhoto: ComposeIds = "button_take_photo" | ||||
|     const val imageIntercom: ComposeIds = "image_intercom" | ||||
|     const val textError: ComposeIds = "text_error" | ||||
|     const val buttonSettings: ComposeIds = "button_settings" | ||||
|     const val buttonHistory: ComposeIds = "button_history" | ||||
| } | ||||
|  | ||||
| object SettingScreenId { | ||||
|     const val screenId: ComposeIds = "setting_screen" | ||||
|     const val inputHouse: ComposeIds = "input_house" | ||||
|     const val inputFlat: ComposeIds = "input_flat" | ||||
|     const val buttonSave: ComposeIds = "button_save" | ||||
| } | ||||
|  | ||||
| object CallScreenId { | ||||
|     const val screenId: ComposeIds = "call_screen" | ||||
|     const val buttonOpen: ComposeIds = "button_open" | ||||
|     const val buttonClose: ComposeIds = "button_close" | ||||
| } | ||||
|  | ||||
| object CallHistoryId { | ||||
|     const val screenId: ComposeIds = "history_screen" | ||||
|     const val recycler: ComposeIds = "recycler" | ||||
|     const val textDate: ComposeIds = "text_date" | ||||
|     const val textStatus: ComposeIds = "text_status" | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| /* НЕ ИЗМЕНЯЙТЕ ФАЙЛ! */ | ||||
| /* ВСЕ ИЗМЕНЕНИЯ ПРИ ТЕСТИРОВАНИИ БУДУТ ОТМЕНЕНЫ */ | ||||
| package ru.samsung.smartintercom.core | ||||
|  | ||||
| object CoreConstants { | ||||
|     const val HOST = "http://89.208.220.227:82/" | ||||
|     const val HEADER_FLAT = "flat" | ||||
|     const val HEADER_HOUSE = "house" | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| package ru.samsung.smartintercom.data.auth.dto | ||||
|  | ||||
| import com.google.gson.annotations.SerializedName | ||||
|  | ||||
| data class AuthDto( | ||||
|     @SerializedName("model") | ||||
|     val model: String | ||||
| ) | ||||
| @@ -0,0 +1,15 @@ | ||||
| package ru.samsung.smartintercom.data.auth.repo | ||||
|  | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.flow.flow | ||||
| import ru.samsung.smartintercom.domain.auth.AuthRepository | ||||
| import ru.samsung.smartintercom.domain.auth.model.AuthEntity | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
|  | ||||
| @Singleton | ||||
| class AuthRepositoryImpl @Inject constructor() : AuthRepository { | ||||
|  | ||||
|     // TODO: Необходимо переопределить праивильно функцию | ||||
|     override val authData: Flow<AuthEntity?> get() = flow { emit(null) } | ||||
| } | ||||
| @@ -0,0 +1,102 @@ | ||||
| /* НЕ ИЗМЕНЯЙТЕ ФАЙЛ! */ | ||||
| /* ВСЕ ИЗМЕНЕНИЯ ПРИ ТЕСТИРОВАНИИ БУДУТ ОТМЕНЕНЫ */ | ||||
| package ru.samsung.smartintercom.data.call | ||||
|  | ||||
| import android.util.Log | ||||
| import com.google.gson.Gson | ||||
| import io.ktor.network.selector.SelectorManager | ||||
| import io.ktor.network.sockets.Socket | ||||
| import io.ktor.network.sockets.aSocket | ||||
| import io.ktor.network.sockets.isClosed | ||||
| import io.ktor.network.sockets.openReadChannel | ||||
| import io.ktor.network.sockets.openWriteChannel | ||||
| import io.ktor.utils.io.writeStringUtf8 | ||||
| import kotlinx.coroutines.CoroutineScope | ||||
| import kotlinx.coroutines.Dispatchers | ||||
| import kotlinx.coroutines.delay | ||||
| import kotlinx.coroutines.flow.MutableSharedFlow | ||||
| import kotlinx.coroutines.flow.asSharedFlow | ||||
| import kotlinx.coroutines.launch | ||||
| import kotlinx.coroutines.withContext | ||||
| import javax.inject.Inject | ||||
| import javax.inject.Singleton | ||||
|  | ||||
| @Singleton | ||||
| class CallDataSource @Inject constructor( | ||||
|     private val gson: Gson | ||||
| ) { | ||||
|     private val _status = MutableSharedFlow<Status>(replay = 1) | ||||
|     val status get() = _status.asSharedFlow() | ||||
|  | ||||
|     private var socketInfo: SocketInfo? = null | ||||
|  | ||||
|  | ||||
|     suspend fun connect(authDto: SocketAuthDto) { | ||||
|         if (socketInfo != null) return | ||||
|  | ||||
|         CoroutineScope(Dispatchers.IO).launch { | ||||
|             try { | ||||
|                 val selectorManager = SelectorManager(Dispatchers.IO) | ||||
|                 val socket = aSocket(selectorManager).tcp().connect(HOSTNAME, PORT) | ||||
|                 socketInfo = SocketInfo( | ||||
|                     socket = socket, | ||||
|                     selectorManager = selectorManager, | ||||
|                 ) | ||||
|  | ||||
|                 val receiveChannel = socket.openReadChannel() | ||||
|                 val sendChannel = socket.openWriteChannel(autoFlush = true) | ||||
|                 sendChannel.writeStringUtf8("${gson.toJson(authDto)}\n") | ||||
|                 _status.emit(Status.Connect) | ||||
|                 while (!socket.isClosed) { | ||||
|                     val message = receiveChannel.readUTF8Line(limit = 100) | ||||
|                     if (message != null) { | ||||
|                         _status.emit(Status.Message(body = message)) | ||||
|                     } else { | ||||
|                         Log.d("Socket", "Server closed a connection") | ||||
|                         close(socket = socket, selectorManager = selectorManager) | ||||
|                     } | ||||
|                 } | ||||
|             } catch (e: Exception) { | ||||
|                 e.printStackTrace() | ||||
|                 delay(TIMEOUT) | ||||
|                 socketInfo = null | ||||
|                 _status.emit(Status.Close) | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     suspend fun close() { | ||||
|         val data = socketInfo ?: return | ||||
|         close(socket = data.socket, selectorManager = data.selectorManager) | ||||
|     } | ||||
|  | ||||
|     private suspend fun close( | ||||
|         socket: Socket, | ||||
|         selectorManager: SelectorManager | ||||
|     ) { | ||||
|         withContext(Dispatchers.IO) { | ||||
|             socket.close() | ||||
|             selectorManager.close() | ||||
|             socketInfo = null | ||||
|             _status.emit(Status.Close) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     sealed interface Status { | ||||
|         data object Connect : Status | ||||
|         data class Message(val body: String) : Status | ||||
|         data object Close : Status | ||||
|     } | ||||
|  | ||||
|     private data class SocketInfo( | ||||
|         val socket: Socket, | ||||
|         val selectorManager: SelectorManager, | ||||
|     ) | ||||
|  | ||||
|     private companion object { | ||||
|         const val HOSTNAME = "89.208.220.227" | ||||
|         const val PORT = 9202 | ||||
|         const val TIMEOUT = 5000L | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,63 @@ | ||||
| /* НЕ ИЗМЕНЯЙТЕ ФАЙЛ! */ | ||||
| /* ВСЕ ИЗМЕНЕНИЯ ПРИ ТЕСТИРОВАНИИ БУДУТ ОТМЕНЕНЫ */ | ||||
| package ru.samsung.smartintercom.data.call | ||||
|  | ||||
| import android.util.Log | ||||
| import dagger.Reusable | ||||
| import kotlinx.coroutines.ExperimentalCoroutinesApi | ||||
| import kotlinx.coroutines.channels.BufferOverflow | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.flow.buffer | ||||
| import kotlinx.coroutines.flow.flatMapLatest | ||||
| import kotlinx.coroutines.flow.map | ||||
| import kotlinx.coroutines.flow.mapNotNull | ||||
| import ru.samsung.smartintercom.domain.auth.AuthRepository | ||||
| import ru.samsung.smartintercom.domain.auth.model.AuthEntity | ||||
| import ru.samsung.smartintercom.domain.call.CallRepository | ||||
| import javax.inject.Inject | ||||
|  | ||||
| @OptIn(ExperimentalCoroutinesApi::class) | ||||
| @Reusable | ||||
| class CallRepositoryImpl @Inject constructor( | ||||
|     authRepo: AuthRepository, | ||||
|     private val callDataSource: CallDataSource, | ||||
| ) : CallRepository { | ||||
|     override val intercomCallStart: Flow<Unit> = authRepo.authData | ||||
|         .flatMapLatest { data -> | ||||
|             if (data == null) { | ||||
|                 callDataSource.close() | ||||
|             } else { | ||||
|                 reconnectSocket(data) | ||||
|             } | ||||
|             callDataSource.status.map { status -> status to data } | ||||
|         } | ||||
|         .mapNotNull { (status, authData) -> | ||||
|             when (status) { | ||||
|                 is CallDataSource.Status.Close -> { | ||||
|                     Log.d(TAG, "Close") | ||||
|                     if (authData != null) reconnectSocket(authData) | ||||
|                     null | ||||
|                 } | ||||
|                 is CallDataSource.Status.Connect -> { | ||||
|                     Log.d(TAG, "Connect") | ||||
|                     null | ||||
|                 } | ||||
|                 is CallDataSource.Status.Message -> { | ||||
|                     Log.d(TAG, "Message") | ||||
|                     Unit | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         .buffer( | ||||
|             capacity = 0, | ||||
|             BufferOverflow.DROP_OLDEST | ||||
|         ) | ||||
|  | ||||
|     private suspend fun reconnectSocket(authEntity: AuthEntity) { | ||||
|         callDataSource.connect(SocketAuthDto(house = authEntity.house, room = authEntity.room)) | ||||
|     } | ||||
|  | ||||
|     private companion object { | ||||
|         const val TAG = "SOCKET" | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,6 @@ | ||||
| package ru.samsung.smartintercom.data.call | ||||
|  | ||||
| data class SocketAuthDto( | ||||
|     val house: String, | ||||
|     val room: String, | ||||
| ) | ||||
| @@ -0,0 +1,13 @@ | ||||
| package ru.samsung.smartintercom.di | ||||
|  | ||||
| import dagger.Component | ||||
| import javax.inject.Singleton | ||||
|  | ||||
| @Component( | ||||
|     modules = [ | ||||
|         AppModule::class, | ||||
|         ContextModule::class, | ||||
|     ] | ||||
| ) | ||||
| @Singleton | ||||
| interface AppComponent : AppProvides | ||||
							
								
								
									
										25
									
								
								app/src/main/java/ru/samsung/smartintercom/di/AppModule.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,25 @@ | ||||
| package ru.samsung.smartintercom.di | ||||
|  | ||||
| import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import dagger.Binds | ||||
| import dagger.Module | ||||
| import dagger.Provides | ||||
| import ru.samsung.smartintercom.data.auth.repo.AuthRepositoryImpl | ||||
| import ru.samsung.smartintercom.data.call.CallRepositoryImpl | ||||
| import ru.samsung.smartintercom.domain.auth.AuthRepository | ||||
| import ru.samsung.smartintercom.domain.call.CallRepository | ||||
|  | ||||
|  | ||||
| @Module | ||||
| interface AppModule { | ||||
|     @Binds | ||||
|     fun authRepo(authRepositoryImpl: AuthRepositoryImpl): AuthRepository | ||||
|     @Binds | ||||
|     fun callRepo(callRepositoryImpl: CallRepositoryImpl): CallRepository | ||||
|  | ||||
|     companion object { | ||||
|         @Provides | ||||
|         fun provideSharedPrefs(context: Context): SharedPreferences = context.getSharedPreferences("PREFS", Context.MODE_PRIVATE) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,9 @@ | ||||
| package ru.samsung.smartintercom.di | ||||
|  | ||||
| import ru.samsung.smartintercom.domain.auth.AuthRepository | ||||
| import ru.samsung.smartintercom.domain.call.CallRepository | ||||
|  | ||||
| interface AppProvides { | ||||
|     fun authRepo(): AuthRepository | ||||
|     fun callRepo(): CallRepository | ||||
| } | ||||
| @@ -0,0 +1,20 @@ | ||||
| package ru.samsung.smartintercom.di | ||||
|  | ||||
| import android.content.Context | ||||
| import com.google.gson.Gson | ||||
| import dagger.Module | ||||
| import dagger.Provides | ||||
| import javax.inject.Singleton | ||||
|  | ||||
|  | ||||
| @Module | ||||
| class ContextModule(val context: Context) { | ||||
|  | ||||
|     @Singleton | ||||
|     @Provides | ||||
|     fun provideContext() = context | ||||
|  | ||||
|     @Singleton | ||||
|     @Provides | ||||
|     fun provideGson() = Gson() | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| package ru.samsung.smartintercom.domain.auth | ||||
|  | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import ru.samsung.smartintercom.domain.auth.model.AuthEntity | ||||
|  | ||||
| interface AuthRepository { | ||||
|     /** | ||||
|      * Данное значение передаёт при каждом изменении новое значение сущности авторизации | ||||
|      * в случае, если пользователь неавторизован - необходимо передать null | ||||
|      */ | ||||
|     val authData: Flow<AuthEntity?> | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| package ru.samsung.smartintercom.domain.auth.model | ||||
|  | ||||
| /** | ||||
|  * Информация о пользователе, необходимой для похода в API | ||||
|  * | ||||
|  * @param house номер дома | ||||
|  * @param room номер комнаты | ||||
|  */ | ||||
| data class AuthEntity( | ||||
|     val house: String, | ||||
|     val room: String, | ||||
| ) | ||||
| @@ -0,0 +1,7 @@ | ||||
| package ru.samsung.smartintercom.domain.call | ||||
|  | ||||
| import kotlinx.coroutines.flow.Flow | ||||
|  | ||||
| interface CallRepository { | ||||
|     val intercomCallStart: Flow<Unit> | ||||
| } | ||||
| @@ -0,0 +1,10 @@ | ||||
| package ru.samsung.smartintercom.domain.call | ||||
|  | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import javax.inject.Inject | ||||
|  | ||||
| class GetCallNeededUseCase @Inject constructor( | ||||
|     private val callRepository: CallRepository | ||||
| ) { | ||||
|     fun execute(): Flow<Unit> = callRepository.intercomCallStart | ||||
| } | ||||
| @@ -0,0 +1,51 @@ | ||||
| package ru.samsung.smartintercom.ui.activity | ||||
|  | ||||
| import android.os.Bundle | ||||
| import androidx.activity.ComponentActivity | ||||
| import androidx.activity.compose.setContent | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.Surface | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.navigation.compose.rememberNavController | ||||
| import ru.samsung.smartintercom.ui.activity.di.MainActivityComponent | ||||
| import ru.samsung.smartintercom.ui.nav.Navigation | ||||
| import ru.samsung.smartintercom.ui.nav.Screen | ||||
| import ru.samsung.smartintercom.ui.nav.navigate | ||||
| import ru.samsung.smartintercom.ui.screen.main.MainScreen | ||||
| import ru.samsung.smartintercom.ui.theme.SmartIntercomTheme | ||||
| import ru.samsung.smartintercom.utils.collectAsEffect | ||||
| import ru.samsung.smartintercom.utils.daggerViewModel | ||||
|  | ||||
| class MainActivity : ComponentActivity() { | ||||
|     override fun onCreate(savedInstanceState: Bundle?) { | ||||
|         super.onCreate(savedInstanceState) | ||||
|         setContent { | ||||
|             SmartIntercomTheme { | ||||
|                 // A surface container using the 'background' color from the theme | ||||
|                 Surface( | ||||
|                     modifier = Modifier.fillMaxSize(), | ||||
|                     color = MaterialTheme.colorScheme.background | ||||
|                 ) { | ||||
|                     val navController = rememberNavController() | ||||
|                     Navigation(navController = navController) | ||||
|                     val component = MainActivityComponent.build() | ||||
|                     val viewModel = daggerViewModel { component.viewModel } | ||||
|                     viewModel.openCallScreen.collectAsEffect { | ||||
|                         navController.navigate(Screen.CALL) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Preview(showBackground = true, device = "spec:width=1080px,height=1920px,dpi=320") | ||||
| @Composable | ||||
| fun GreetingPreview() { | ||||
|     SmartIntercomTheme { | ||||
|         MainScreen.Render(rememberNavController()) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| package ru.samsung.smartintercom.ui.activity | ||||
|  | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import kotlinx.coroutines.flow.SharedFlow | ||||
| import kotlinx.coroutines.flow.asSharedFlow | ||||
| import kotlinx.coroutines.launch | ||||
| import ru.samsung.smartintercom.domain.call.GetCallNeededUseCase | ||||
| import ru.samsung.smartintercom.utils.MutablePublishFlow | ||||
| import javax.inject.Inject | ||||
|  | ||||
| class MainActivityViewModel @Inject constructor( | ||||
|     private val getCallNeededUseCase: GetCallNeededUseCase | ||||
| ) : ViewModel() { | ||||
|     val openCallScreen: SharedFlow<Unit> get() = _openCallScreen.asSharedFlow() | ||||
|     private val _openCallScreen = MutablePublishFlow<Unit>() | ||||
|  | ||||
|     init { | ||||
|         viewModelScope.launch { | ||||
|             getCallNeededUseCase.execute().collect { | ||||
|                 _openCallScreen.emit(Unit) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| package ru.samsung.smartintercom.ui.activity.di | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import dagger.Component | ||||
| import ru.samsung.smartintercom.App | ||||
| import ru.samsung.smartintercom.di.AppComponent | ||||
| import ru.samsung.smartintercom.ui.activity.MainActivityViewModel | ||||
|  | ||||
| @Component(dependencies = [AppComponent::class]) | ||||
| @MainActivityScope | ||||
| interface MainActivityComponent { | ||||
|  | ||||
|     @Component.Factory | ||||
|     interface Factory { | ||||
|         fun create(appComponent: AppComponent): MainActivityComponent | ||||
|     } | ||||
|  | ||||
|     val viewModel: MainActivityViewModel | ||||
|  | ||||
|     companion object { | ||||
|         @Composable | ||||
|         fun build(): MainActivityComponent { | ||||
|             return DaggerMainActivityComponent.factory().create( | ||||
|                 (LocalContext.current.applicationContext as App).appComponent | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| package ru.samsung.smartintercom.ui.activity.di | ||||
|  | ||||
| import javax.inject.Scope | ||||
|  | ||||
|  | ||||
| @Scope | ||||
| @Retention(AnnotationRetention.RUNTIME) | ||||
| annotation class MainActivityScope | ||||
| @@ -0,0 +1,34 @@ | ||||
| package ru.samsung.smartintercom.ui.nav | ||||
|  | ||||
| import androidx.compose.animation.EnterTransition | ||||
| import androidx.compose.animation.ExitTransition | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.navigation.NavController | ||||
| import androidx.navigation.NavHostController | ||||
| import androidx.navigation.compose.NavHost | ||||
| import androidx.navigation.compose.composable | ||||
|  | ||||
| /** | ||||
|  * Данная функция инициализирует граф инициализации | ||||
|  */ | ||||
| @Composable | ||||
| fun Navigation(navController: NavHostController) { | ||||
|     NavHost( | ||||
|         navController = navController, | ||||
|         startDestination = Screen.MAIN.route, | ||||
|         enterTransition = { EnterTransition.None }, | ||||
|         exitTransition = { ExitTransition.None } | ||||
|     ) | ||||
|     { | ||||
|         // Здесь происходит перебор функций для инициализации всех возможных экранов | ||||
|         Screen.entries.forEach { screen -> | ||||
|             composable(route = screen.route) { | ||||
|                 screen.baseScreen.Render(navController) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| fun NavController.navigate(screen: Screen) { | ||||
|     navigate(route = screen.route) | ||||
| } | ||||
							
								
								
									
										16
									
								
								app/src/main/java/ru/samsung/smartintercom/ui/nav/Screen.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,16 @@ | ||||
| package ru.samsung.smartintercom.ui.nav | ||||
|  | ||||
| import ru.samsung.smartintercom.ui.screen.ScreenBaseData | ||||
| import ru.samsung.smartintercom.ui.screen.call.CallScreen | ||||
| import ru.samsung.smartintercom.ui.screen.main.MainScreen | ||||
|  | ||||
| /** | ||||
|  * Класс, в котором перечислены все экраны | ||||
|  * | ||||
|  * @param route путь до экрана | ||||
|  * @param baseScreen сущность экрана, которая будет отрендерена при открытии | ||||
|  */ | ||||
| enum class Screen(val route: String, internal val baseScreen: ScreenBaseData) { | ||||
|     MAIN("main", MainScreen), | ||||
|     CALL("call", CallScreen), | ||||
| } | ||||
| @@ -0,0 +1,22 @@ | ||||
| package ru.samsung.smartintercom.ui.screen | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.navigation.NavController | ||||
|  | ||||
| /** | ||||
|  * Базовый интерфейс экрана | ||||
|  */ | ||||
| interface ScreenBaseData { | ||||
|     /** | ||||
|      * Название экрана | ||||
|      */ | ||||
|     val name: String | ||||
|  | ||||
|     /** | ||||
|      * Функция, вызываемая при отрисовки экрана | ||||
|      * | ||||
|      * @param navController контроллер, который необходим для открытия других экранов | ||||
|      */ | ||||
|     @Composable | ||||
|     fun Render(navController: NavController) | ||||
| } | ||||
| @@ -0,0 +1,135 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.call | ||||
|  | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.RowScope | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.fillMaxWidth | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.width | ||||
| import androidx.compose.foundation.layout.wrapContentHeight | ||||
| import androidx.compose.material3.Button | ||||
| import androidx.compose.material3.ButtonDefaults | ||||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.Scaffold | ||||
| import androidx.compose.material3.Text | ||||
| 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.platform.testTag | ||||
| import androidx.compose.ui.res.colorResource | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.text.style.TextAlign | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.navigation.NavController | ||||
| import androidx.navigation.NavDestination.Companion.hierarchy | ||||
| import androidx.navigation.compose.rememberNavController | ||||
| import ru.samsung.smartintercom.R | ||||
| import ru.samsung.smartintercom.core.CallScreenId | ||||
| import ru.samsung.smartintercom.ui.nav.Screen | ||||
| import ru.samsung.smartintercom.ui.nav.navigate | ||||
| import ru.samsung.smartintercom.ui.screen.ScreenBaseData | ||||
| import ru.samsung.smartintercom.ui.screen.call.di.CallScreenComponent | ||||
| import ru.samsung.smartintercom.ui.theme.SmartIntercomTheme | ||||
| import ru.samsung.smartintercom.ui.theme.button | ||||
| import ru.samsung.smartintercom.utils.collectAsEffect | ||||
| import ru.samsung.smartintercom.utils.daggerViewModel | ||||
| import ru.samsung.smartintercom.utils.setupScreenData | ||||
|  | ||||
| /** | ||||
|  * Экран вызова | ||||
|  * | ||||
|  * ВНИМАНИЕ! | ||||
|  * Данный экран приведён для примера. | ||||
|  * Вы можете его модифицировать любыми удобными способами и дорабатывать его. | ||||
|  */ | ||||
| object CallScreen : ScreenBaseData { | ||||
|     override val name = CallScreenId.screenId | ||||
|  | ||||
|     @OptIn(ExperimentalMaterial3Api::class) | ||||
|     @Composable | ||||
|     override fun Render(navController: NavController) { | ||||
|         val component = CallScreenComponent.build() | ||||
|         val viewModel = daggerViewModel { component.viewModel } | ||||
|         viewModel.goBackOrOpenMainScreen.collectAsEffect { | ||||
|             if (navController.graph.hierarchy.count() > 0) { | ||||
|                 navController.popBackStack() | ||||
|             } else { | ||||
|                 navController.navigate(Screen.MAIN) | ||||
|             } | ||||
|         } | ||||
|         Scaffold( | ||||
|             modifier = Modifier.setupScreenData(CallScreen), | ||||
|             content = { paddingValues -> | ||||
|                 Column( | ||||
|                     modifier = Modifier | ||||
|                         .padding(paddingValues = paddingValues) | ||||
|                         .padding(16.dp), | ||||
|                 ) { | ||||
|                     Text( | ||||
|                         modifier = Modifier | ||||
|                             .weight(1f) | ||||
|                             .fillMaxWidth() | ||||
|                             .wrapContentHeight(align = Alignment.CenterVertically), | ||||
|                         text = stringResource(R.string.call_screen_title), | ||||
|                         style = MaterialTheme.typography.displayLarge, | ||||
|                         textAlign = TextAlign.Center, | ||||
|                     ) | ||||
|                     Row( | ||||
|                         modifier = Modifier | ||||
|                             .fillMaxWidth(), | ||||
|                         horizontalArrangement = Arrangement.SpaceEvenly, | ||||
|                     ) { | ||||
|                         Button( | ||||
|                             id = CallScreenId.buttonOpen, | ||||
|                             text = stringResource(R.string.open_intercom), | ||||
|                             color = colorResource(R.color.call_open), | ||||
|                             onClick = viewModel::clickOpen, | ||||
|                         ) | ||||
|                         Spacer(modifier = Modifier.width(16.dp)) | ||||
|                         Button( | ||||
|                             id = CallScreenId.buttonClose, | ||||
|                             text = stringResource(R.string.decline_intercom), | ||||
|                             color = colorResource(R.color.call_close), | ||||
|                             onClick = viewModel::clickDecline, | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @Composable | ||||
|     fun RowScope.Button( | ||||
|         id: String, | ||||
|         text: String, | ||||
|         color: Color, | ||||
|         onClick: () -> Unit | ||||
|     ) { | ||||
|         Button( | ||||
|             modifier = Modifier | ||||
|                 .testTag(id) | ||||
|                 .weight(1.0f, true), | ||||
|             colors = ButtonDefaults.buttonColors(containerColor = color), | ||||
|             onClick = onClick, | ||||
|         ) { | ||||
|             Text( | ||||
|                 style = MaterialTheme.typography.button.large, | ||||
|                 text = text, | ||||
|             ) | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Preview(showBackground = true, device = "spec:width=1080px,height=1920px,dpi=320") | ||||
| @Composable | ||||
| fun CallScreenPreview() { | ||||
|     SmartIntercomTheme { | ||||
|         CallScreen.Render(rememberNavController()) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,27 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.call | ||||
|  | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import kotlinx.coroutines.flow.SharedFlow | ||||
| import kotlinx.coroutines.flow.asSharedFlow | ||||
| import kotlinx.coroutines.launch | ||||
| import ru.samsung.smartintercom.utils.MutablePublishFlow | ||||
| import javax.inject.Inject | ||||
|  | ||||
| class CallViewModel @Inject constructor() : ViewModel() { | ||||
|     val goBackOrOpenMainScreen: SharedFlow<Unit> get() = _goBackOrOpenMainScreen.asSharedFlow() | ||||
|     private val _goBackOrOpenMainScreen = MutablePublishFlow<Unit>() | ||||
|     fun clickOpen() { | ||||
|         viewModelScope.launch { | ||||
|             // TODO: необходимо добавить обработку нажатия на кнопку открытия | ||||
|             _goBackOrOpenMainScreen.emit(Unit) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun clickDecline() { | ||||
|         viewModelScope.launch { | ||||
|             // TODO: необходимо добавить обработку нажатия на кнопку закрытия | ||||
|             _goBackOrOpenMainScreen.emit(Unit) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.call.di | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import dagger.Component | ||||
| import ru.samsung.smartintercom.App | ||||
| import ru.samsung.smartintercom.di.AppComponent | ||||
| import ru.samsung.smartintercom.ui.screen.call.CallViewModel | ||||
|  | ||||
| @Component(dependencies = [AppComponent::class]) | ||||
| @CallScreenScope | ||||
| interface CallScreenComponent { | ||||
|  | ||||
|     @Component.Factory | ||||
|     interface Factory { | ||||
|         fun create(appComponent: AppComponent): CallScreenComponent | ||||
|     } | ||||
|  | ||||
|     val viewModel: CallViewModel | ||||
|  | ||||
|     companion object { | ||||
|         @Composable | ||||
|         fun build(): CallScreenComponent { | ||||
|             return DaggerCallScreenComponent.factory().create( | ||||
|                 (LocalContext.current.applicationContext as App).appComponent | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.call.di | ||||
|  | ||||
| import javax.inject.Scope | ||||
|  | ||||
|  | ||||
| @Scope | ||||
| @Retention(AnnotationRetention.RUNTIME) | ||||
| annotation class CallScreenScope | ||||
| @@ -0,0 +1,131 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.main | ||||
|  | ||||
| import androidx.compose.foundation.layout.Arrangement | ||||
| import androidx.compose.foundation.layout.Box | ||||
| import androidx.compose.foundation.layout.Column | ||||
| import androidx.compose.foundation.layout.Row | ||||
| import androidx.compose.foundation.layout.Spacer | ||||
| import androidx.compose.foundation.layout.fillMaxSize | ||||
| import androidx.compose.foundation.layout.padding | ||||
| import androidx.compose.foundation.layout.width | ||||
| import androidx.compose.material3.Button | ||||
| import androidx.compose.material3.ExperimentalMaterial3Api | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.Scaffold | ||||
| import androidx.compose.material3.Text | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.collectAsState | ||||
| import androidx.compose.runtime.getValue | ||||
| import androidx.compose.ui.Alignment | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.platform.testTag | ||||
| import androidx.compose.ui.res.stringResource | ||||
| import androidx.compose.ui.text.style.TextAlign | ||||
| import androidx.compose.ui.tooling.preview.Preview | ||||
| import androidx.compose.ui.unit.dp | ||||
| import androidx.navigation.NavController | ||||
| import ru.samsung.smartintercom.R | ||||
| import ru.samsung.smartintercom.core.MainScreenId | ||||
| import ru.samsung.smartintercom.ui.screen.ScreenBaseData | ||||
| import ru.samsung.smartintercom.ui.screen.main.di.MainScreenComponent | ||||
| import ru.samsung.smartintercom.ui.theme.SmartIntercomTheme | ||||
| import ru.samsung.smartintercom.ui.theme.button | ||||
| import ru.samsung.smartintercom.utils.daggerViewModel | ||||
| import ru.samsung.smartintercom.utils.setupScreenData | ||||
|  | ||||
| object MainScreen : ScreenBaseData { | ||||
|     override val name = MainScreenId.screenId | ||||
|  | ||||
|     @Composable | ||||
|     override fun Render(navController: NavController) { | ||||
|         val component = MainScreenComponent.build() | ||||
|         val viewModel = daggerViewModel { component.viewModel } | ||||
|         val state by viewModel.uiState.collectAsState() | ||||
|         RenderState( | ||||
|             state = state, | ||||
|             openSettingClick = {}, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @OptIn(ExperimentalMaterial3Api::class) | ||||
|     @Composable | ||||
|     internal fun RenderState( | ||||
|         state: MainState, | ||||
|         openSettingClick: () -> Unit, | ||||
|     ) { | ||||
|         Scaffold( | ||||
|             modifier = Modifier.setupScreenData(this), | ||||
|             content = { paddingValues -> | ||||
|                 Box( | ||||
|                     modifier = Modifier | ||||
|                         .fillMaxSize() | ||||
|                         .padding(paddingValues) | ||||
|                 ) { | ||||
|                     when (state) { | ||||
|                         is MainState.Intro -> { | ||||
|                             IntroState( | ||||
|                                 openSettingClick = openSettingClick | ||||
|                             ) | ||||
|                         } | ||||
|  | ||||
|                         is MainState.Loading -> TODO("Добавить состояние загрузки") | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @Composable | ||||
|     private fun IntroState( | ||||
|         openSettingClick: () -> Unit, | ||||
|     ) { | ||||
|         Column( | ||||
|             modifier = Modifier | ||||
|                 .fillMaxSize() | ||||
|                 .padding(16.dp), | ||||
|             horizontalAlignment = Alignment.CenterHorizontally, | ||||
|         ) { | ||||
|             Column( | ||||
|                 modifier = Modifier | ||||
|                     .weight(1f), | ||||
|                 horizontalAlignment = Alignment.CenterHorizontally, | ||||
|                 verticalArrangement = Arrangement.Center | ||||
|             ) { | ||||
|                 Text( | ||||
|                     text = stringResource(R.string.main_screen_intro_title), | ||||
|                     style = MaterialTheme.typography.displayLarge, | ||||
|                     textAlign = TextAlign.Center, | ||||
|                 ) | ||||
|                 Spacer(modifier = Modifier.width(8.dp)) | ||||
|                 Text( | ||||
|                     text = stringResource(R.string.main_screen_intro_text), | ||||
|                     style = MaterialTheme.typography.headlineLarge, | ||||
|                     textAlign = TextAlign.Center, | ||||
|                 ) | ||||
|             } | ||||
|             Button( | ||||
|                 modifier = Modifier.testTag(MainScreenId.buttonStart), | ||||
|                 onClick = openSettingClick | ||||
|             ) { | ||||
|                 Text( | ||||
|                     text = stringResource(id = R.string.main_screen_intro_button), | ||||
|                     style = MaterialTheme.typography.button.normal | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @Preview(showBackground = true, device = "spec:width=1080px,height=1920px,dpi=320") | ||||
| @Composable | ||||
| fun RenderPreview() { | ||||
|     val mockAction = {} | ||||
|     SmartIntercomTheme { | ||||
|         Row { | ||||
|             MainScreen.RenderState( | ||||
|                 state = MainState.Intro, | ||||
|                 openSettingClick = mockAction, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,7 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.main | ||||
|  | ||||
| sealed interface MainState { | ||||
|     data object Intro: MainState | ||||
|     data object Loading: MainState | ||||
|     // TODO: необходимо добавить состояние ошибки и отображения | ||||
| } | ||||
| @@ -0,0 +1,22 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.main | ||||
|  | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.viewModelScope | ||||
| import kotlinx.coroutines.ExperimentalCoroutinesApi | ||||
| import kotlinx.coroutines.flow.MutableStateFlow | ||||
| import kotlinx.coroutines.flow.StateFlow | ||||
| import kotlinx.coroutines.flow.asStateFlow | ||||
| import kotlinx.coroutines.launch | ||||
| import javax.inject.Inject | ||||
|  | ||||
| @OptIn(ExperimentalCoroutinesApi::class) | ||||
| class MainViewModel @Inject constructor() : ViewModel() { | ||||
|     val uiState: StateFlow<MainState> get() = _uiState.asStateFlow() | ||||
|     private val _uiState = MutableStateFlow<MainState>(MainState.Loading) | ||||
|  | ||||
|     init { | ||||
|         viewModelScope.launch { | ||||
|             _uiState.emit(MainState.Intro) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,29 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.main.di | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import dagger.Component | ||||
| import ru.samsung.smartintercom.App | ||||
| import ru.samsung.smartintercom.di.AppComponent | ||||
| import ru.samsung.smartintercom.ui.screen.main.MainViewModel | ||||
|  | ||||
| @Component(dependencies = [AppComponent::class]) | ||||
| @MainScreenScope | ||||
| interface MainScreenComponent { | ||||
|  | ||||
|     @Component.Factory | ||||
|     interface Factory { | ||||
|         fun create(appComponent: AppComponent): MainScreenComponent | ||||
|     } | ||||
|  | ||||
|     val viewModel: MainViewModel | ||||
|  | ||||
|     companion object { | ||||
|         @Composable | ||||
|         fun build(): MainScreenComponent { | ||||
|             return DaggerMainScreenComponent.factory().create( | ||||
|                 (LocalContext.current.applicationContext as App).appComponent | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,8 @@ | ||||
| package ru.samsung.smartintercom.ui.screen.main.di | ||||
|  | ||||
| import javax.inject.Scope | ||||
|  | ||||
|  | ||||
| @Scope | ||||
| @Retention(AnnotationRetention.RUNTIME) | ||||
| annotation class MainScreenScope | ||||
							
								
								
									
										11
									
								
								app/src/main/java/ru/samsung/smartintercom/ui/theme/Color.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,11 @@ | ||||
| package ru.samsung.smartintercom.ui.theme | ||||
|  | ||||
| import androidx.compose.ui.graphics.Color | ||||
|  | ||||
| val Purple80 = Color(0xFFD0BCFF) | ||||
| val PurpleGrey80 = Color(0xFFCCC2DC) | ||||
| val Pink80 = Color(0xFFEFB8C8) | ||||
|  | ||||
| val Purple40 = Color(0xFF6650a4) | ||||
| val PurpleGrey40 = Color(0xFF625b71) | ||||
| val Pink40 = Color(0xFF7D5260) | ||||
							
								
								
									
										60
									
								
								app/src/main/java/ru/samsung/smartintercom/ui/theme/Theme.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,60 @@ | ||||
| package ru.samsung.smartintercom.ui.theme | ||||
|  | ||||
| import android.app.Activity | ||||
| import android.os.Build | ||||
| import androidx.compose.foundation.isSystemInDarkTheme | ||||
| import androidx.compose.material3.MaterialTheme | ||||
| import androidx.compose.material3.darkColorScheme | ||||
| import androidx.compose.material3.dynamicDarkColorScheme | ||||
| import androidx.compose.material3.dynamicLightColorScheme | ||||
| import androidx.compose.material3.lightColorScheme | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.SideEffect | ||||
| import androidx.compose.ui.graphics.toArgb | ||||
| import androidx.compose.ui.platform.LocalContext | ||||
| import androidx.compose.ui.platform.LocalView | ||||
| import androidx.core.view.WindowCompat | ||||
|  | ||||
| private val DarkColorScheme = darkColorScheme( | ||||
|     primary = Purple80, | ||||
|     secondary = PurpleGrey80, | ||||
|     tertiary = Pink80 | ||||
| ) | ||||
|  | ||||
| private val LightColorScheme = lightColorScheme( | ||||
|     primary = Purple40, | ||||
|     secondary = PurpleGrey40, | ||||
|     tertiary = Pink40 | ||||
| ) | ||||
|  | ||||
| @Composable | ||||
| fun SmartIntercomTheme( | ||||
|     darkTheme: Boolean = isSystemInDarkTheme(), | ||||
|     // Dynamic color is available on Android 12+ | ||||
|     dynamicColor: Boolean = true, | ||||
|     content: @Composable () -> Unit | ||||
| ) { | ||||
|     val colorScheme = when { | ||||
|         dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { | ||||
|             val context = LocalContext.current | ||||
|             if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) | ||||
|         } | ||||
|  | ||||
|         darkTheme -> DarkColorScheme | ||||
|         else -> LightColorScheme | ||||
|     } | ||||
|     val view = LocalView.current | ||||
|     if (!view.isInEditMode) { | ||||
|         SideEffect { | ||||
|             val window = (view.context as Activity).window | ||||
|             window.statusBarColor = colorScheme.primary.toArgb() | ||||
|             WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     MaterialTheme( | ||||
|         colorScheme = colorScheme, | ||||
|         typography = Typography, | ||||
|         content = content | ||||
|     ) | ||||
| } | ||||
							
								
								
									
										37
									
								
								app/src/main/java/ru/samsung/smartintercom/ui/theme/Type.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,37 @@ | ||||
| package ru.samsung.smartintercom.ui.theme | ||||
|  | ||||
| import androidx.compose.material3.Typography | ||||
| import androidx.compose.ui.text.TextStyle | ||||
| import androidx.compose.ui.text.font.FontFamily | ||||
| import androidx.compose.ui.text.font.FontWeight | ||||
| import androidx.compose.ui.unit.sp | ||||
|  | ||||
| // Set of Material typography styles to start with | ||||
| val Typography = Typography( | ||||
|     bodyLarge = TextStyle( | ||||
|         fontFamily = FontFamily.Default, | ||||
|         fontWeight = FontWeight.Normal, | ||||
|         fontSize = 16.sp, | ||||
|         lineHeight = 24.sp, | ||||
|         letterSpacing = 0.5.sp | ||||
|     ) | ||||
| ) | ||||
|  | ||||
| val Typography.button get() = Button | ||||
|  | ||||
| object Button { | ||||
|     val large = base.copy( | ||||
|         fontSize = 32.sp, | ||||
|         lineHeight = 36.sp, | ||||
|     ) | ||||
|  | ||||
|     val normal = base.copy( | ||||
|         fontSize = 16.sp, | ||||
|         lineHeight = 24.sp, | ||||
|     ) | ||||
|  | ||||
|     private val base get() = TextStyle( | ||||
|         fontFamily = FontFamily.Default, | ||||
|         fontWeight = FontWeight.Medium, | ||||
|     ) | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| package ru.samsung.smartintercom.utils | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.compose.runtime.LaunchedEffect | ||||
| import kotlinx.coroutines.flow.Flow | ||||
| import kotlinx.coroutines.flow.flowOn | ||||
| import kotlinx.coroutines.flow.launchIn | ||||
| import kotlinx.coroutines.flow.onEach | ||||
| import kotlin.coroutines.CoroutineContext | ||||
| import kotlin.coroutines.EmptyCoroutineContext | ||||
|  | ||||
| @Suppress("ComposableNaming", "StateFlowValueCalledInComposition") | ||||
| @Composable | ||||
| fun <T> Flow<T>.collectAsEffect( | ||||
|     context: CoroutineContext = EmptyCoroutineContext, | ||||
|     block: (T) -> Unit | ||||
| ) { | ||||
|     LaunchedEffect(key1 = Unit) { | ||||
|         onEach(block).flowOn(context).launchIn(this) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| package ru.samsung.smartintercom.utils | ||||
|  | ||||
| import androidx.compose.runtime.Composable | ||||
| import androidx.lifecycle.ViewModel | ||||
| import androidx.lifecycle.ViewModelProvider | ||||
| import androidx.lifecycle.viewmodel.compose.viewModel | ||||
|  | ||||
|  | ||||
| @Composable | ||||
| inline fun <reified T : ViewModel> daggerViewModel( | ||||
|     key: String? = null, | ||||
|     crossinline viewModelInstanceCreator: () -> T | ||||
| ): T { | ||||
|     return viewModel( | ||||
|         modelClass = T::class.java, | ||||
|         key = key, | ||||
|         factory = object : ViewModelProvider.Factory { | ||||
|             override fun <T : ViewModel> create(modelClass: Class<T>): T { | ||||
|                 return viewModelInstanceCreator() as T | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| } | ||||
| @@ -0,0 +1,10 @@ | ||||
| package ru.samsung.smartintercom.utils | ||||
|  | ||||
| import kotlinx.coroutines.channels.BufferOverflow | ||||
| import kotlinx.coroutines.flow.MutableSharedFlow | ||||
|  | ||||
| fun <T> MutablePublishFlow() = MutableSharedFlow<T>( | ||||
|     replay = 0, | ||||
|     extraBufferCapacity = 1, | ||||
|     BufferOverflow.DROP_OLDEST | ||||
| ) | ||||
| @@ -0,0 +1,17 @@ | ||||
| package ru.samsung.smartintercom.utils | ||||
|  | ||||
| import androidx.compose.runtime.Stable | ||||
| import androidx.compose.ui.ExperimentalComposeUiApi | ||||
| import androidx.compose.ui.Modifier | ||||
| import androidx.compose.ui.platform.testTag | ||||
| import androidx.compose.ui.semantics.semantics | ||||
| import androidx.compose.ui.semantics.testTagsAsResourceId | ||||
| import ru.samsung.smartintercom.ui.screen.ScreenBaseData | ||||
|  | ||||
| @OptIn(ExperimentalComposeUiApi::class) | ||||
| @Stable | ||||
| fun Modifier.setupScreenData(screenBaseData: ScreenBaseData) = semantics { | ||||
|     testTagsAsResourceId = true | ||||
| } | ||||
|     .testTag(tag = screenBaseData.name) | ||||
|  | ||||
							
								
								
									
										170
									
								
								app/src/main/res/drawable/ic_launcher_background.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,170 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:width="108dp" | ||||
|     android:height="108dp" | ||||
|     android:viewportHeight="108" | ||||
|     android:viewportWidth="108"> | ||||
|     <path | ||||
|         android:fillColor="#3DDC84" | ||||
|         android:pathData="M0,0h108v108h-108z" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M9,0L9,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,0L19,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M29,0L29,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M39,0L39,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M49,0L49,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M59,0L59,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M69,0L69,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M79,0L79,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M89,0L89,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M99,0L99,108" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,9L108,9" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,19L108,19" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,29L108,29" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,39L108,39" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,49L108,49" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,59L108,59" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,69L108,69" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,79L108,79" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,89L108,89" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M0,99L108,99" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,29L89,29" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,39L89,39" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,49L89,49" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,59L89,59" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,69L89,69" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M19,79L89,79" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M29,19L29,89" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M39,19L39,89" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M49,19L49,89" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M59,19L59,89" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M69,19L69,89" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
|     <path | ||||
|         android:fillColor="#00000000" | ||||
|         android:pathData="M79,19L79,89" | ||||
|         android:strokeColor="#33FFFFFF" | ||||
|         android:strokeWidth="0.8" /> | ||||
| </vector> | ||||
							
								
								
									
										30
									
								
								app/src/main/res/drawable/ic_launcher_foreground.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,30 @@ | ||||
| <vector xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:aapt="http://schemas.android.com/aapt" | ||||
|     android:width="108dp" | ||||
|     android:height="108dp" | ||||
|     android:viewportHeight="108" | ||||
|     android:viewportWidth="108"> | ||||
|     <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> | ||||
|         <aapt:attr name="android:fillColor"> | ||||
|             <gradient | ||||
|                 android:endX="85.84757" | ||||
|                 android:endY="92.4963" | ||||
|                 android:startX="42.9492" | ||||
|                 android:startY="49.59793" | ||||
|                 android:type="linear"> | ||||
|                 <item | ||||
|                     android:color="#44000000" | ||||
|                     android:offset="0.0" /> | ||||
|                 <item | ||||
|                     android:color="#00000000" | ||||
|                     android:offset="1.0" /> | ||||
|             </gradient> | ||||
|         </aapt:attr> | ||||
|     </path> | ||||
|     <path | ||||
|         android:fillColor="#FFFFFF" | ||||
|         android:fillType="nonZero" | ||||
|         android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" | ||||
|         android:strokeColor="#00000000" | ||||
|         android:strokeWidth="1" /> | ||||
| </vector> | ||||
							
								
								
									
										6
									
								
								app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <background android:drawable="@drawable/ic_launcher_background" /> | ||||
|     <foreground android:drawable="@drawable/ic_launcher_foreground" /> | ||||
|     <monochrome android:drawable="@drawable/ic_launcher_foreground" /> | ||||
| </adaptive-icon> | ||||
							
								
								
									
										6
									
								
								app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> | ||||
|     <background android:drawable="@drawable/ic_launcher_background" /> | ||||
|     <foreground android:drawable="@drawable/ic_launcher_foreground" /> | ||||
|     <monochrome android:drawable="@drawable/ic_launcher_foreground" /> | ||||
| </adaptive-icon> | ||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 982 B | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 5.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 7.6 KiB | 
							
								
								
									
										12
									
								
								app/src/main/res/values/colors.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,12 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <color name="purple_200">#FFBB86FC</color> | ||||
|     <color name="purple_500">#FF6200EE</color> | ||||
|     <color name="purple_700">#FF3700B3</color> | ||||
|     <color name="teal_200">#FF03DAC5</color> | ||||
|     <color name="teal_700">#FF018786</color> | ||||
|     <color name="black">#FF000000</color> | ||||
|     <color name="white">#FFFFFFFF</color> | ||||
|     <color name="call_open">#43A047</color> | ||||
|     <color name="call_close">#E53935</color> | ||||
| </resources> | ||||
							
								
								
									
										28
									
								
								app/src/main/res/values/ids.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,28 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <!-- НЕ ИЗМЕНЯЙТЕ ДАННЫЙ ФАЙЛ --> | ||||
| <!-- ПРИ ТЕСТИРОВАНИИ ФАЙЛ БУДЕТ ВОЗВРАЩЁН В ИСХОДНОЕ СОСТОЯНИЕ --> | ||||
| <resources> | ||||
|     <!-- Main Screen --> | ||||
|     <item name="button_start" type="id"/> | ||||
|     <item name="button_retry" type="id"/> | ||||
|     <item name="text_intercom_model" type="id"/> | ||||
|     <item name="button_take_photo" type="id"/> | ||||
|     <item name="image_intercom" type="id"/> | ||||
|     <item name="text_error" type="id"/> | ||||
|     <item name="button_settings" type="id"/> | ||||
|     <item name="button_history" type="id"/> | ||||
|  | ||||
|     <!-- Setting Screen --> | ||||
|     <item name="input_house" type="id"/> | ||||
|     <item name="input_flat" type="id"/> | ||||
|     <item name="button_save" type="id"/> | ||||
|  | ||||
|     <!-- Call Screen --> | ||||
|     <item name="button_open" type="id"/> | ||||
|     <item name="button_close" type="id"/> | ||||
|  | ||||
|     <!-- Call History --> | ||||
|     <item name="recycler" type="id"/> | ||||
|     <item name="text_date" type="id"/> | ||||
|     <item name="text_status" type="id"/> | ||||
| </resources> | ||||
							
								
								
									
										13
									
								
								app/src/main/res/values/strings.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | ||||
| <resources> | ||||
|     <string name="app_name">SmartIntercom</string> | ||||
|  | ||||
|     <!-- Call Screen --> | ||||
|     <string name="call_screen_title">Incoming call</string> | ||||
|     <string name="open_intercom">Open</string> | ||||
|     <string name="decline_intercom">Decline</string> | ||||
|  | ||||
|     <!-- Main Screen --> | ||||
|     <string name="main_screen_intro_title">Welcome</string> | ||||
|     <string name="main_screen_intro_text">Setup your intercom for working app</string> | ||||
|     <string name="main_screen_intro_button">Go to setting</string> | ||||
| </resources> | ||||
							
								
								
									
										4
									
								
								app/src/main/res/values/themes.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <resources> | ||||
|     <style name="Theme.SmartIntercom" parent="android:Theme.Material.Light.NoActionBar" /> | ||||
| </resources> | ||||
							
								
								
									
										13
									
								
								app/src/main/res/xml/backup_rules.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,13 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?><!-- | ||||
|    Sample backup rules file; uncomment and customize as necessary. | ||||
|    See https://developer.android.com/guide/topics/data/autobackup | ||||
|    for details. | ||||
|    Note: This file is ignored for devices older that API 31 | ||||
|    See https://developer.android.com/about/versions/12/backup-restore | ||||
| --> | ||||
| <full-backup-content> | ||||
|     <!-- | ||||
|    <include domain="sharedpref" path="."/> | ||||
|    <exclude domain="sharedpref" path="device.xml"/> | ||||
| --> | ||||
| </full-backup-content> | ||||
							
								
								
									
										19
									
								
								app/src/main/res/xml/data_extraction_rules.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,19 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?><!-- | ||||
|    Sample data extraction rules file; uncomment and customize as necessary. | ||||
|    See https://developer.android.com/about/versions/12/backup-restore#xml-changes | ||||
|    for details. | ||||
| --> | ||||
| <data-extraction-rules> | ||||
|     <cloud-backup> | ||||
|         <!-- TODO: Use <include> and <exclude> to control what is backed up. | ||||
|         <include .../> | ||||
|         <exclude .../> | ||||
|         --> | ||||
|     </cloud-backup> | ||||
|     <!-- | ||||
|     <device-transfer> | ||||
|         <include .../> | ||||
|         <exclude .../> | ||||
|     </device-transfer> | ||||
|     --> | ||||
| </data-extraction-rules> | ||||