11. Kotlin- Persistencia de información en el dispositivo y modo oscuro

Pantalla de configuración

Lo primero que se realizar es la creación de la pantalla de configuración.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_dark"
            app:tint="@color/texto"
            android:layout_margin="16dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Modo oscuro"
            android:layout_weight="1"
            android:textColor="@color/texto"
        android:layout_margin="16dp"/>

        <Switch
            android:id="@+id/switchDarkMod"
            android:layout_width="50dp"
            android:layout_height="wrap_content"
        android:layout_margin="16dp"/>
    </LinearLayout>
    <com.google.android.material.divider.MaterialDivider
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginVertical="5dp"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_volume"
            app:tint="@color/texto"
            android:layout_margin="16dp"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Volumen"
            android:textColor="@color/texto"
            android:layout_margin="16dp"/>

        <com.google.android.material.slider.RangeSlider
            android:id="@+id/rsVolume"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginHorizontal="10dp"
            android:stepSize="1"
            android:valueFrom="0"
            android:valueTo="100"/>


    </LinearLayout>


</LinearLayout>

Grabación de la información

Es necesario modificar el gradel para meter la dependencia al DataStore

    //DataStore
   implementation ("androidx.datastore:datastore-preferences:1.0.0")

A continuación, realizar la grabación de la información de pantalla, siempre en una coroutina.

package com.javi.u11v

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Switch
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.android.material.slider.RangeSlider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")


class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val switchDarkMod=findViewById<Switch>(R.id.switchDarkMod)
        val rsVolume = findViewById<RangeSlider>(R.id.rsVolume)

 



        //GRABACIÓN
        switchDarkMod.setOnCheckedChangeListener { compoundButton, b ->
            Log.i("JAI","El valor es $b")

            CoroutineScope(Dispatchers.IO).launch {
                dataStore.edit {
                    it[booleanPreferencesKey("OSCURE_MODE")]=b
                }
            }
        }

        rsVolume.addOnChangeListener { slider, value, fromUser ->
            Log.i("JAI","El volumen es $value")
            CoroutineScope(Dispatchers.IO).launch {
                dataStore.edit {
                    it[intPreferencesKey("VOLUME")]=value.toInt()
                }
            }
        }


    }


}

Lectura de la información

Será necesario realizar la lectura y guardar la información en los controles. Hay que tener la precaución de los controles muy dinámicos que la lectura y grabación de los valores hará inestable el control, con lo que hay que poner una variable para controlar que solo lo haga una vez.

package com.javi.u11v

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Switch
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.android.material.slider.RangeSlider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")
var firstTime=true

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val switchDarkMod=findViewById<Switch>(R.id.switchDarkMod)
        val rsVolume = findViewById<RangeSlider>(R.id.rsVolume)

        //LECTURA
        CoroutineScope(Dispatchers.IO).launch {
            val darkMode: Flow<Boolean> = dataStore.data.map{
                it[booleanPreferencesKey("OSCURE_MODE")]?:false
            }
            darkMode.collect{
                runOnUiThread{
                    switchDarkMod.isChecked=it
                }
            }
        }

        CoroutineScope(Dispatchers.IO).launch {
            val volume: Flow<Int> = dataStore.data.map{
                it[intPreferencesKey("VOLUME")]?:50
            }
            volume.filter{firstTime}.collect{
                runOnUiThread{
                    rsVolume.setValues(it.toFloat())
                    firstTime=!firstTime
                }
            }
        }



        //GRABACIÓN
        switchDarkMod.setOnCheckedChangeListener { compoundButton, b ->
            Log.i("JAI","El valor es $b")


            CoroutineScope(Dispatchers.IO).launch {
                dataStore.edit {
                    it[booleanPreferencesKey("OSCURE_MODE")]=b
                }
            }
        }

        rsVolume.addOnChangeListener { slider, value, fromUser ->
            Log.i("JAI","El volumen es $value")
            CoroutineScope(Dispatchers.IO).launch {
                dataStore.edit {
                    it[intPreferencesKey("VOLUME")]=value.toInt()
                }
            }
        }


    }


}

Modo oscuro

Vemos como podemos activar y desactivar el modo oscuro en el momento de activar y desactivar el control

package com.javi.u11v

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Switch
import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO
import androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import com.google.android.material.slider.RangeSlider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

val Context.dataStore: DataStore<Preferences> by preferencesDataStore("settings")
var firstTime=true

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val switchDarkMod=findViewById<Switch>(R.id.switchDarkMod)
        val rsVolume = findViewById<RangeSlider>(R.id.rsVolume)

        //LECTURA
        CoroutineScope(Dispatchers.IO).launch {
            val darkMode: Flow<Boolean> = dataStore.data.map{
                it[booleanPreferencesKey("OSCURE_MODE")]?:false
            }
            darkMode.collect{
                runOnUiThread{
                    switchDarkMod.isChecked=it
                    if (it) enableDarkMode()
                    else disableDarkMode()
                }
            }
        }

        CoroutineScope(Dispatchers.IO).launch {
            val volume: Flow<Int> = dataStore.data.map{
                it[intPreferencesKey("VOLUME")]?:50
            }
            volume.filter{firstTime}.collect{
                runOnUiThread{
                    rsVolume.setValues(it.toFloat())
                    firstTime=!firstTime
                }
            }
        }



        //GRABACIÓN
        switchDarkMod.setOnCheckedChangeListener { compoundButton, b ->
            Log.i("JAI","El valor es $b")
            if (b) enableDarkMode()
            else disableDarkMode()

            CoroutineScope(Dispatchers.IO).launch {
                dataStore.edit {
                    it[booleanPreferencesKey("OSCURE_MODE")]=b
                }
            }
        }

        rsVolume.addOnChangeListener { slider, value, fromUser ->
            Log.i("JAI","El volumen es $value")
            CoroutineScope(Dispatchers.IO).launch {
                dataStore.edit {
                    it[intPreferencesKey("VOLUME")]=value.toInt()
                }
            }
        }


    }

    private fun disableDarkMode() {
        AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)
        delegate.applyDayNight()
    }

    private fun enableDarkMode() {
        AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES)
        delegate.applyDayNight()
    }
}

Información recopila de Aristidevs

Related Posts