Commit 3da122cb authored by Mark Murphy's avatar Mark Murphy

updates for API changes and dark theme support

parent 7398a123
......@@ -28,12 +28,13 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation "androidx.appcompat:appcompat:1.1.0-beta01"
implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
implementation "androidx.cardview:cardview:$cardview_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.recyclerview:recyclerview:$recyclerview_version"
implementation "androidx.preference:preference-ktx:$preference_version"
implementation "org.koin:koin-core:$koin_version"
implementation "org.koin:koin-android:$koin_version"
implementation "org.koin:koin-androidx-viewmodel:$koin_version"
......
......@@ -23,7 +23,7 @@ import org.koin.androidx.viewmodel.ext.koin.viewModel
import org.koin.dsl.module.module
private val KOIN_MODULE = module {
viewModel { TypeInfoViewModel(androidContext()) }
viewModel { MainMotor(androidContext()) }
}
class KoinApp : Application() {
......
......@@ -18,8 +18,11 @@ package com.commonsware.android.q.typeinfo
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.observe
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
......@@ -29,7 +32,11 @@ import com.commonsware.android.q.typeinfo.databinding.RowBinding
import org.koin.androidx.viewmodel.ext.android.viewModel
class MainActivity : AppCompatActivity() {
private val vm: TypeInfoViewModel by viewModel()
private val motor: MainMotor by viewModel()
private lateinit var light: MenuItem
private lateinit var dark: MenuItem
private lateinit var system: MenuItem
private lateinit var currentThemeMode: ThemeMode
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
......@@ -41,11 +48,71 @@ class MainActivity : AppCompatActivity() {
rv.layoutManager = LinearLayoutManager(this)
rv.adapter = adapter
vm.types.observe(this) { types -> adapter.submitList(types) }
motor.states.observe(this) { state ->
adapter.submitList(state.types)
currentThemeMode = state.themeMode
if (::light.isInitialized) {
updateThemeMode(currentThemeMode)
}
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.themes, menu)
light = menu.findItem(R.id.light)
dark = menu.findItem(R.id.dark)
system = menu.findItem(R.id.system)
if (::currentThemeMode.isInitialized) updateThemeMode(currentThemeMode)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item) {
light -> {
light.isChecked = true
motor.setThemeMode(ThemeMode.LIGHT)
return true
}
dark -> {
dark.isChecked = true
motor.setThemeMode(ThemeMode.DARK)
return true
}
system -> {
system.isChecked = true
motor.setThemeMode(ThemeMode.SYSTEM)
return true
}
}
return super.onOptionsItemSelected(item)
}
private fun updateThemeMode(themeMode: ThemeMode) {
when (themeMode) {
ThemeMode.LIGHT -> {
light.isChecked = true
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
}
ThemeMode.DARK -> {
dark.isChecked = true
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
ThemeMode.SYSTEM -> {
system.isChecked = true
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
}
}
}
}
class TypeAdapter(private val inflater: LayoutInflater) : ListAdapter<TypeRecord, RowHolder>(TypeRecordDiffer) {
class TypeAdapter(private val inflater: LayoutInflater) :
ListAdapter<RowState, RowHolder>(TypeRecordDiffer) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
RowHolder(RowBinding.inflate(inflater, parent, false))
......@@ -56,16 +123,16 @@ class TypeAdapter(private val inflater: LayoutInflater) : ListAdapter<TypeRecord
class RowHolder(private val binding: RowBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(type: TypeRecord) {
fun bind(type: RowState) {
binding.setType(type)
binding.icon.setImageIcon(type.icon)
}
}
object TypeRecordDiffer : DiffUtil.ItemCallback<TypeRecord>() {
override fun areItemsTheSame(oldItem: TypeRecord, newItem: TypeRecord) =
object TypeRecordDiffer : DiffUtil.ItemCallback<RowState>() {
override fun areItemsTheSame(oldItem: RowState, newItem: RowState) =
oldItem.type == newItem.type
override fun areContentsTheSame(oldItem: TypeRecord, newItem: TypeRecord) =
override fun areContentsTheSame(oldItem: RowState, newItem: RowState) =
areItemsTheSame(oldItem, newItem)
}
\ No newline at end of file
......@@ -21,6 +21,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
......@@ -93,21 +94,44 @@ private val MIME_TYPES = listOf(
"rutabaga/marshmallow"
)
class TypeInfoViewModel(context: Context) : ViewModel() {
private val _types = MutableLiveData<List<TypeRecord>>()
val types: LiveData<List<TypeRecord>> = _types
enum class ThemeMode {
LIGHT, DARK, SYSTEM
}
private const val PREF_THEME_MODE = "themeMode"
class MainMotor(context: Context) : ViewModel() {
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
private val _states = MutableLiveData<MainViewState>()
val states: LiveData<MainViewState> = _states
init {
viewModelScope.launch { _types.postValue(mapTypes(context)) }
viewModelScope.launch(Dispatchers.Main) {
_states.postValue(
MainViewState(
mapTypes(context),
loadInitialThemeMode()
)
)
}
}
fun setThemeMode(mode: ThemeMode) {
prefs.edit().putString(PREF_THEME_MODE, mode.name).apply()
_states.value?.let { _states.value = it.copy(themeMode = mode) }
}
private suspend fun loadInitialThemeMode() = withContext(Dispatchers.IO) {
prefs.getString(PREF_THEME_MODE, null)?.let { ThemeMode.valueOf(it) }
?: ThemeMode.SYSTEM
}
private suspend fun mapTypes(context: Context): List<TypeRecord> {
return withContext(Dispatchers.Default) {
private suspend fun mapTypes(context: Context) =
withContext(Dispatchers.Default) {
val resolver = context.contentResolver
MIME_TYPES
.map { TypeRecord(it, resolver.getTypeInfo(it)) }
.map { RowState(it, resolver.getTypeInfo(it)) }
.sortedBy { it.description.toString() }
}
}
}
\ No newline at end of file
/*
Copyright (c) 2019 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _Elements of Android Q
https://commonsware.com/AndroidQ
*/
package com.commonsware.android.q.typeinfo
data class MainViewState(
val types: List<RowState>,
val themeMode: ThemeMode
)
\ No newline at end of file
......@@ -19,7 +19,7 @@ package com.commonsware.android.q.typeinfo
import android.content.ContentResolver
import android.graphics.drawable.Icon
data class TypeRecord(
data class RowState(
val type: String,
val label: CharSequence,
val description: CharSequence,
......
......@@ -5,7 +5,7 @@
<variable
name="type"
type="com.commonsware.android.q.typeinfo.TypeRecord" />
type="com.commonsware.android.q.typeinfo.RowState" />
</data>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
......
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:id="@+id/theme"
android:checkableBehavior="single">
<item
android:id="@+id/light"
android:title="@string/menu_light" />
<item
android:id="@+id/dark"
android:title="@string/menu_dark" />
<item
android:id="@+id/system"
android:checked="true"
android:title="@string/menu_system" />
</group>
</menu>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FFC107</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
<color name="colorPrimary">#ffc107</color>
<color name="colorPrimaryDark">#ffaa00</color>
<color name="colorAccent">#536dfe</color>
</resources>
<resources>
<string name="app_name">TypeInfo</string>
<string name="icon">icon</string>
<string name="menu_light">Light</string>
<string name="menu_dark">Dark</string>
<string name="menu_system">System</string>
</resources>
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="Theme.AppCompat.DayNight">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment