From bb730f2f0b2ae5ddcd8b333c064f1faee85441ce Mon Sep 17 00:00:00 2001 From: Hyperling Date: Sun, 29 Dec 2024 14:53:12 +0000 Subject: [PATCH] First Working Revision (#1) App is now in working order. Reviewed-on: https://gitea.com/Hyperling/example-android-database-room/pulls/1 Co-authored-by: Hyperling Co-committed-by: Hyperling --- README.md | 10 +- app/build.gradle.kts | 25 ++-- app/src/main/AndroidManifest.xml | 9 +- .../hyperling/roomexample/AddContactDialog.kt | 73 ++++++++++++ .../com/hyperling/roomexample/ContactDao.kt | 4 + .../com/hyperling/roomexample/ContactEvent.kt | 2 +- .../hyperling/roomexample/ContactScreen.kt | 108 ++++++++++++++++++ .../com/hyperling/roomexample/ContactState.kt | 10 ++ .../hyperling/roomexample/ContactViewModel.kt | 95 +++++++++++++++ .../hyperling/roomexample/FirstFragment.kt | 44 ------- .../com/hyperling/roomexample/MainActivity.kt | 99 ++++++++-------- .../hyperling/roomexample/SecondFragment.kt | 44 ------- .../hyperling/roomexample/ui/theme/Color.kt | 11 ++ .../hyperling/roomexample/ui/theme/Theme.kt | 57 +++++++++ .../hyperling/roomexample/ui/theme/Type.kt | 34 ++++++ app/src/main/res/layout/activity_main.xml | 33 ------ app/src/main/res/layout/content_main.xml | 19 --- app/src/main/res/layout/fragment_first.xml | 35 ------ app/src/main/res/layout/fragment_second.xml | 35 ------ app/src/main/res/menu/menu_main.xml | 10 -- app/src/main/res/mipmap-hdpi/icon_v001.png | Bin 0 -> 12183 bytes .../main/res/mipmap-hdpi/icon_v001_round.png | Bin 0 -> 29357 bytes app/src/main/res/navigation/nav_graph.xml | 28 ----- app/src/main/res/values-land/dimens.xml | 3 - app/src/main/res/values-night/themes.xml | 7 -- app/src/main/res/values-v23/themes.xml | 9 -- app/src/main/res/values-w1240dp/dimens.xml | 3 - app/src/main/res/values-w600dp/dimens.xml | 3 - app/src/main/res/values/colors.xml | 5 + app/src/main/res/values/dimens.xml | 3 - app/src/main/res/values/strings.xml | 45 +------- app/src/main/res/values/themes.xml | 10 +- build.gradle.kts | 1 + gradle/libs.versions.toml | 32 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- media/icon_v001.png | Bin 0 -> 12183 bytes media/icon_v001.xcf | Bin 0 -> 32063 bytes media/icon_v001_round.png | Bin 0 -> 29357 bytes settings.gradle.kts | 2 +- 39 files changed, 504 insertions(+), 406 deletions(-) create mode 100644 app/src/main/java/com/hyperling/roomexample/AddContactDialog.kt create mode 100644 app/src/main/java/com/hyperling/roomexample/ContactScreen.kt create mode 100644 app/src/main/java/com/hyperling/roomexample/ContactState.kt create mode 100644 app/src/main/java/com/hyperling/roomexample/ContactViewModel.kt delete mode 100644 app/src/main/java/com/hyperling/roomexample/FirstFragment.kt delete mode 100644 app/src/main/java/com/hyperling/roomexample/SecondFragment.kt create mode 100644 app/src/main/java/com/hyperling/roomexample/ui/theme/Color.kt create mode 100644 app/src/main/java/com/hyperling/roomexample/ui/theme/Theme.kt create mode 100644 app/src/main/java/com/hyperling/roomexample/ui/theme/Type.kt delete mode 100644 app/src/main/res/layout/activity_main.xml delete mode 100644 app/src/main/res/layout/content_main.xml delete mode 100644 app/src/main/res/layout/fragment_first.xml delete mode 100644 app/src/main/res/layout/fragment_second.xml delete mode 100644 app/src/main/res/menu/menu_main.xml create mode 100644 app/src/main/res/mipmap-hdpi/icon_v001.png create mode 100644 app/src/main/res/mipmap-hdpi/icon_v001_round.png delete mode 100644 app/src/main/res/navigation/nav_graph.xml delete mode 100644 app/src/main/res/values-land/dimens.xml delete mode 100644 app/src/main/res/values-night/themes.xml delete mode 100644 app/src/main/res/values-v23/themes.xml delete mode 100644 app/src/main/res/values-w1240dp/dimens.xml delete mode 100644 app/src/main/res/values-w600dp/dimens.xml delete mode 100644 app/src/main/res/values/dimens.xml create mode 100644 media/icon_v001.png create mode 100644 media/icon_v001.xcf create mode 100644 media/icon_v001_round.png diff --git a/README.md b/README.md index df03549..92cb348 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,9 @@ -# example-android-database-room +# Example: Android-Database-Room -Example app of using Room functionality to handle databases. \ No newline at end of file +Example app of using Room functionality within Android to handle databases. + +# Resources + +This app was created while following this tutorial: + +https://www.youtube.com/watch?v=bOd3wO0uFr8&themeRefresh=1 \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 60a083b..c767aeb 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) // For "Room", see below for more details. kotlin("kapt") @@ -8,11 +9,11 @@ plugins { android { namespace = "com.hyperling.roomexample" - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "com.hyperling.roomexample" - minSdk = 19 + minSdk = 21 targetSdk = 34 versionCode = 1 versionName = "1.0" @@ -37,21 +38,27 @@ android { jvmTarget = "11" } buildFeatures { - viewBinding = true + compose = true } } dependencies { implementation(libs.androidx.core.ktx) - implementation(libs.androidx.appcompat) - implementation(libs.material) - implementation(libs.androidx.constraintlayout) - implementation(libs.androidx.navigation.fragment.ktx) - implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + implementation(platform(libs.androidx.compose.bom)) + implementation(libs.androidx.ui) + implementation(libs.androidx.ui.graphics) + implementation(libs.androidx.ui.tooling.preview) + implementation(libs.androidx.material3) testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) + androidTestImplementation(platform(libs.androidx.compose.bom)) + androidTestImplementation(libs.androidx.ui.test.junit4) + debugImplementation(libs.androidx.ui.tooling) + debugImplementation(libs.androidx.ui.test.manifest) // For Room // https://www.youtube.com/watch?v=bOd3wO0uFr8&themeRefresh=1 @@ -61,7 +68,7 @@ dependencies { kapt "androidx.room:room-compiler:$room_version" // */ /* build.grade.kts version */ - val room_version: String = "2.5.0" + val room_version: String = "2.6.1" implementation("androidx.room:room-ktx:$room_version") kapt("androidx.room:room-compiler:$room_version") // */ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 884c060..84fa0ee 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,16 +6,17 @@ android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" - android:icon="@mipmap/ic_launcher" + android:icon="@mipmap/icon_v001" android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" + android:roundIcon="@mipmap/icon_v001_round" android:supportsRtl="true" - android:theme="@style/Theme.RoomExample" + android:theme="@style/Theme.ExampleRoomDatabase21Empty" tools:targetApi="31"> + android:label="@string/app_name" + android:theme="@style/Theme.ExampleRoomDatabase21Empty"> diff --git a/app/src/main/java/com/hyperling/roomexample/AddContactDialog.kt b/app/src/main/java/com/hyperling/roomexample/AddContactDialog.kt new file mode 100644 index 0000000..391517e --- /dev/null +++ b/app/src/main/java/com/hyperling/roomexample/AddContactDialog.kt @@ -0,0 +1,73 @@ +package com.hyperling.roomexample + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun AddContactDialog( + state: ContactState, + onEvent: (ContactEvent) -> Unit, + modifier: Modifier = Modifier +) { + AlertDialog( + onDismissRequest = { + onEvent(ContactEvent.HideDialog) + }, + title = { Text(text = "Add Contact") }, + text = { + Column ( + verticalArrangement = Arrangement.spacedBy(8.dp) + ){ + TextField( + value = state.firstName, + onValueChange = { + onEvent(ContactEvent.SetFirstName(it)) + }, + placeholder = { + Text(text = "First Name") + } + ) + TextField( + value = state.lastName, + onValueChange = { + onEvent(ContactEvent.SetLastName(it)) + }, + placeholder = { + Text(text = "Last Name") + } + ) + TextField( + value = state.phoneNumber, + onValueChange = { + onEvent(ContactEvent.SetPhoneNumber(it)) + }, + placeholder = { + Text(text = "Phone Number") + } + ) + } + }, + confirmButton = { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Button(onClick = { + onEvent(ContactEvent.SaveContact) + }) { + Text(text = "Save") + } + } + } + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/ContactDao.kt b/app/src/main/java/com/hyperling/roomexample/ContactDao.kt index 6c0d380..a7e0dca 100644 --- a/app/src/main/java/com/hyperling/roomexample/ContactDao.kt +++ b/app/src/main/java/com/hyperling/roomexample/ContactDao.kt @@ -9,6 +9,9 @@ import kotlinx.coroutines.flow.Flow @Dao interface ContactDao { + // 2024-12-28 + // If these complain about return type errors, try upgrading + // the ROOM version, otherwise remove the SUSPEND keyword. @Upsert suspend fun upsertContact(contact: Contact) @@ -23,4 +26,5 @@ interface ContactDao { @Query("SELECT * FROM contact ORDER BY phoneNumber ASC") fun selectContactsPN(): Flow> + } \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/ContactEvent.kt b/app/src/main/java/com/hyperling/roomexample/ContactEvent.kt index b575b56..5692a2a 100644 --- a/app/src/main/java/com/hyperling/roomexample/ContactEvent.kt +++ b/app/src/main/java/com/hyperling/roomexample/ContactEvent.kt @@ -8,5 +8,5 @@ sealed interface ContactEvent { object ShowDialog: ContactEvent object HideDialog: ContactEvent data class SortContacts(val sortType: SortType): ContactEvent - data class DeleteContact(val ) + data class DeleteContact(val contact: Contact): ContactEvent } \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/ContactScreen.kt b/app/src/main/java/com/hyperling/roomexample/ContactScreen.kt new file mode 100644 index 0000000..542dc58 --- /dev/null +++ b/app/src/main/java/com/hyperling/roomexample/ContactScreen.kt @@ -0,0 +1,108 @@ +package com.hyperling.roomexample + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.FloatingActionButton +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.RadioButton +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.unit.dp +import androidx.compose.ui.unit.sp + +@Composable +fun ContactScreen( + state: ContactState, + onEvent: (ContactEvent) -> Unit +){ + Scaffold ( + floatingActionButton = { + FloatingActionButton(onClick = { + onEvent(ContactEvent.ShowDialog) + }) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add Contact", + ) + } + } + ){ padding -> + if(state.isAddingContact){ + AddContactDialog(state, onEvent) + } + + LazyColumn ( + contentPadding = padding, + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.spacedBy(16.dp) + ){ + item { + Row ( + modifier = Modifier + .fillMaxWidth() + .horizontalScroll(rememberScrollState()), + verticalAlignment = Alignment.CenterVertically + ){ + SortType.values().forEach { sortType -> + Row ( + modifier = Modifier + .clickable { + onEvent(ContactEvent.SortContacts(sortType)) + }, + verticalAlignment = Alignment.CenterVertically + ){ + RadioButton( + selected = state.sortType == sortType, + onClick = { + onEvent(ContactEvent.SortContacts(sortType)) + } + ) + Text(text = sortType.name) + } + } + } + } + items(state.contacts) {contact -> + Row ( + modifier = Modifier.fillMaxWidth() + ) { + Column ( + modifier = Modifier.weight(1f) + ) { + Text ( + text = "${contact.firstName} ${contact.lastName}", + fontSize = 20.sp + ) + Text( + text = contact.phoneNumber, + fontSize = 12.sp + ) + } + IconButton(onClick = { + onEvent(ContactEvent.DeleteContact(contact)) + }) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = "Delete Contact" + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/ContactState.kt b/app/src/main/java/com/hyperling/roomexample/ContactState.kt new file mode 100644 index 0000000..426dcaa --- /dev/null +++ b/app/src/main/java/com/hyperling/roomexample/ContactState.kt @@ -0,0 +1,10 @@ +package com.hyperling.roomexample + +data class ContactState( + val contacts: List = emptyList(), + val firstName: String = "", + val lastName: String = "", + val phoneNumber: String = "", + val isAddingContact: Boolean = false, + val sortType: SortType = SortType.LAST_NAME, +) diff --git a/app/src/main/java/com/hyperling/roomexample/ContactViewModel.kt b/app/src/main/java/com/hyperling/roomexample/ContactViewModel.kt new file mode 100644 index 0000000..901547d --- /dev/null +++ b/app/src/main/java/com/hyperling/roomexample/ContactViewModel.kt @@ -0,0 +1,95 @@ +package com.hyperling.roomexample + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +class ContactViewModel ( + private val dao: ContactDao, +): ViewModel() { + + private val _sortType = MutableStateFlow(SortType.LAST_NAME) + private val _state = MutableStateFlow(ContactState()) + + private val _contacts = _sortType + .flatMapLatest { sortType -> + when(sortType) { + SortType.FIRST_NAME -> dao.selectContactsFN() + SortType.LAST_NAME -> dao.selectContactsLN() + SortType.PHONE_NUMBER -> dao.selectContactsPN() + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList()) + + val state = combine(_state, _sortType, _contacts) { state, sortType, contacts -> + state.copy( + contacts = contacts, + sortType = sortType + ) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ContactState()) + + fun onEvent(event: ContactEvent) { + when(event) { + is ContactEvent.DeleteContact -> { + viewModelScope.launch { + dao.deleteContact(event.contact) + } + } + ContactEvent.HideDialog -> { + _state.update { it.copy( + isAddingContact = false + ) } + } + ContactEvent.SaveContact -> { + val firstName = state.value.firstName + val lastName = state.value.lastName + val phoneNumber = state.value.phoneNumber + + if (firstName.isBlank() || lastName.isBlank() || phoneNumber.isBlank()) { + return + } + + val contact = Contact(firstName, lastName, phoneNumber) + + viewModelScope.launch { + dao.upsertContact(contact) + } + + _state.update { it.copy( + isAddingContact = false, + firstName = "", + lastName = "", + phoneNumber = "", + ) } + } + is ContactEvent.SetFirstName -> { + _state.update { it.copy( + firstName = event.firstName + ) } + } + is ContactEvent.SetLastName -> { + _state.update { it.copy( + lastName = event.lastName + ) } + } + is ContactEvent.SetPhoneNumber -> { + _state.update { it.copy( + phoneNumber = event.phoneNumber + ) } + } + ContactEvent.ShowDialog -> { + _state.update { it.copy( + isAddingContact = true + ) } + } + is ContactEvent.SortContacts -> { + _sortType.value = event.sortType + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/FirstFragment.kt b/app/src/main/java/com/hyperling/roomexample/FirstFragment.kt deleted file mode 100644 index e9ce58f..0000000 --- a/app/src/main/java/com/hyperling/roomexample/FirstFragment.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.hyperling.roomexample - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.navigation.fragment.findNavController -import com.hyperling.roomexample.databinding.FragmentFirstBinding - -/** - * A simple [Fragment] subclass as the default destination in the navigation. - */ -class FirstFragment : Fragment() { - - private var _binding: FragmentFirstBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - - _binding = FragmentFirstBinding.inflate(inflater, container, false) - return binding.root - - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.buttonFirst.setOnClickListener { - findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment) - } - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/MainActivity.kt b/app/src/main/java/com/hyperling/roomexample/MainActivity.kt index 158b2fc..1bcd6ce 100644 --- a/app/src/main/java/com/hyperling/roomexample/MainActivity.kt +++ b/app/src/main/java/com/hyperling/roomexample/MainActivity.kt @@ -1,59 +1,64 @@ package com.hyperling.roomexample import android.os.Bundle -import com.google.android.material.snackbar.Snackbar -import androidx.appcompat.app.AppCompatActivity -import androidx.navigation.findNavController -import androidx.navigation.ui.AppBarConfiguration -import androidx.navigation.ui.navigateUp -import androidx.navigation.ui.setupActionBarWithNavController -import android.view.Menu -import android.view.MenuItem -import com.hyperling.roomexample.databinding.ActivityMainBinding +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +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.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.room.Room +import com.hyperling.roomexample.ui.theme.ExampleRoomDatabase21EmptyTheme -class MainActivity : AppCompatActivity() { +class MainActivity : ComponentActivity() { - private lateinit var appBarConfiguration: AppBarConfiguration - private lateinit var binding: ActivityMainBinding + private val _db by lazy { + Room.databaseBuilder( + applicationContext, + ContactDatabase::class.java, + "contacts.db" + ).build() + } + private val _viewModel by viewModels ( + factoryProducer = { + object : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return ContactViewModel(_db.dao) as T + } + } + } + ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - - binding = ActivityMainBinding.inflate(layoutInflater) - setContentView(binding.root) - - setSupportActionBar(binding.toolbar) - - val navController = findNavController(R.id.nav_host_fragment_content_main) - appBarConfiguration = AppBarConfiguration(navController.graph) - setupActionBarWithNavController(navController, appBarConfiguration) - - binding.fab.setOnClickListener { view -> - Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) - .setAction("Action", null) - .setAnchorView(R.id.fab).show() + enableEdgeToEdge() + setContent { + ExampleRoomDatabase21EmptyTheme { + val state by _viewModel.state.collectAsState() + ContactScreen( + state = state, + onEvent = _viewModel::onEvent + ) + } } } +} - override fun onCreateOptionsMenu(menu: Menu): Boolean { - // Inflate the menu; this adds items to the action bar if it is present. - menuInflater.inflate(R.menu.menu_main, menu) - return true +/* * / +@Preview(showBackground = true) +@Composable +fun GreetingPreview() { + ExampleRoomDatabase21EmptyTheme { + Greeting("Android") } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - return when (item.itemId) { - R.id.action_settings -> true - else -> super.onOptionsItemSelected(item) - } - } - - override fun onSupportNavigateUp(): Boolean { - val navController = findNavController(R.id.nav_host_fragment_content_main) - return navController.navigateUp(appBarConfiguration) - || super.onSupportNavigateUp() - } -} \ No newline at end of file +} +// */ \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/SecondFragment.kt b/app/src/main/java/com/hyperling/roomexample/SecondFragment.kt deleted file mode 100644 index 5275cab..0000000 --- a/app/src/main/java/com/hyperling/roomexample/SecondFragment.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.hyperling.roomexample - -import android.os.Bundle -import androidx.fragment.app.Fragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.navigation.fragment.findNavController -import com.hyperling.roomexample.databinding.FragmentSecondBinding - -/** - * A simple [Fragment] subclass as the second destination in the navigation. - */ -class SecondFragment : Fragment() { - - private var _binding: FragmentSecondBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - - _binding = FragmentSecondBinding.inflate(inflater, container, false) - return binding.root - - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.buttonSecond.setOnClickListener { - findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment) - } - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/ui/theme/Color.kt b/app/src/main/java/com/hyperling/roomexample/ui/theme/Color.kt new file mode 100644 index 0000000..bcf2d47 --- /dev/null +++ b/app/src/main/java/com/hyperling/roomexample/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.hyperling.roomexample.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) \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/ui/theme/Theme.kt b/app/src/main/java/com/hyperling/roomexample/ui/theme/Theme.kt new file mode 100644 index 0000000..88ee194 --- /dev/null +++ b/app/src/main/java/com/hyperling/roomexample/ui/theme/Theme.kt @@ -0,0 +1,57 @@ +package com.hyperling.roomexample.ui.theme + +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.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun ExampleRoomDatabase21EmptyTheme( + 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 + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/hyperling/roomexample/ui/theme/Type.kt b/app/src/main/java/com/hyperling/roomexample/ui/theme/Type.kt new file mode 100644 index 0000000..438a9b0 --- /dev/null +++ b/app/src/main/java/com/hyperling/roomexample/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.hyperling.roomexample.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 + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 16313e0..0000000 --- a/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml deleted file mode 100644 index e416e1c..0000000 --- a/app/src/main/res/layout/content_main.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_first.xml b/app/src/main/res/layout/fragment_first.xml deleted file mode 100644 index 44baecd..0000000 --- a/app/src/main/res/layout/fragment_first.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - -