{"id":574,"date":"2023-12-23T07:40:59","date_gmt":"2023-12-23T07:40:59","guid":{"rendered":"https:\/\/javigomez.org\/?p=574"},"modified":"2025-09-29T20:33:44","modified_gmt":"2025-09-29T20:33:44","slug":"11-kotlin-persistencia-de-informacion-en-el-dispositivo-y-modo-oscuro","status":"publish","type":"post","link":"https:\/\/javigomez.org\/index.php\/2023\/12\/23\/11-kotlin-persistencia-de-informacion-en-el-dispositivo-y-modo-oscuro\/","title":{"rendered":"11. Kotlin- Persistencia de informaci\u00f3n en el dispositivo y modo oscuro"},"content":{"rendered":"\n<p><strong>Pantalla de configuraci\u00f3n<\/strong><\/p>\n\n\n\n<p>Lo primero que se realizar es la creaci\u00f3n de la pantalla de configuraci\u00f3n.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" src=\"http:\/\/javigomez.org\/wp-content\/uploads\/2023\/12\/image-6-2-1-1.png\" alt=\"\" class=\"wp-image-575\"\/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;\n&lt;LinearLayout xmlns:android=\"http:\/\/schemas.android.com\/apk\/res\/android\"\n    xmlns:app=\"http:\/\/schemas.android.com\/apk\/res-auto\"\n    xmlns:tools=\"http:\/\/schemas.android.com\/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"&gt;\n\n    &lt;LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"&gt;\n        &lt;ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable\/ic_dark\"\n            app:tint=\"@color\/texto\"\n            android:layout_margin=\"16dp\"\/&gt;\n        &lt;TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"Modo oscuro\"\n            android:layout_weight=\"1\"\n            android:textColor=\"@color\/texto\"\n        android:layout_margin=\"16dp\"\/&gt;\n\n        &lt;Switch\n            android:id=\"@+id\/switchDarkMod\"\n            android:layout_width=\"50dp\"\n            android:layout_height=\"wrap_content\"\n        android:layout_margin=\"16dp\"\/&gt;\n    &lt;\/LinearLayout&gt;\n    &lt;com.google.android.material.divider.MaterialDivider\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1dp\"\n        android:layout_marginVertical=\"5dp\"\/&gt;\n    &lt;LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:orientation=\"horizontal\"&gt;\n        &lt;ImageView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:src=\"@drawable\/ic_volume\"\n            app:tint=\"@color\/texto\"\n            android:layout_margin=\"16dp\"\/&gt;\n        &lt;TextView\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"Volumen\"\n            android:textColor=\"@color\/texto\"\n            android:layout_margin=\"16dp\"\/&gt;\n\n        &lt;com.google.android.material.slider.RangeSlider\n            android:id=\"@+id\/rsVolume\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginHorizontal=\"10dp\"\n            android:stepSize=\"1\"\n            android:valueFrom=\"0\"\n            android:valueTo=\"100\"\/&gt;\n\n\n    &lt;\/LinearLayout&gt;\n\n\n&lt;\/LinearLayout&gt;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Kotlin U11 01 Pantalla Configuracion\" width=\"1200\" height=\"675\" src=\"https:\/\/www.youtube.com\/embed\/K4lScpVvSIs?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p>Grabaci\u00f3n de la informaci\u00f3n<\/p>\n\n\n\n<p>Es necesario modificar el gradel para meter la dependencia al DataStore<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    \/\/DataStore\n   implementation (\"androidx.datastore:datastore-preferences:1.0.0\")<\/code><\/pre>\n\n\n\n<p>A continuaci\u00f3n, realizar la grabaci\u00f3n de la informaci\u00f3n de pantalla, siempre en una coroutina.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.javi.u11v\n\nimport android.content.Context\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport android.util.Log\nimport android.widget.Switch\nimport androidx.appcompat.app.AppCompatDelegate\nimport androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO\nimport androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.booleanPreferencesKey\nimport androidx.datastore.preferences.core.edit\nimport androidx.datastore.preferences.core.intPreferencesKey\nimport androidx.datastore.preferences.preferencesDataStore\nimport com.google.android.material.slider.RangeSlider\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.collect\nimport kotlinx.coroutines.flow.filter\nimport kotlinx.coroutines.flow.map\nimport kotlinx.coroutines.launch\n\nval Context.dataStore: DataStore&lt;Preferences&gt; by preferencesDataStore(\"settings\")\n\n\nclass MainActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n\n        val switchDarkMod=findViewById&lt;Switch&gt;(R.id.switchDarkMod)\n        val rsVolume = findViewById&lt;RangeSlider&gt;(R.id.rsVolume)\n\n \n\n\n\n        \/\/GRABACI\u00d3N\n        switchDarkMod.setOnCheckedChangeListener { compoundButton, b -&gt;\n            Log.i(\"JAI\",\"El valor es $b\")\n\n            CoroutineScope(Dispatchers.IO).launch {\n                dataStore.edit {\n                    it&#91;booleanPreferencesKey(\"OSCURE_MODE\")]=b\n                }\n            }\n        }\n\n        rsVolume.addOnChangeListener { slider, value, fromUser -&gt;\n            Log.i(\"JAI\",\"El volumen es $value\")\n            CoroutineScope(Dispatchers.IO).launch {\n                dataStore.edit {\n                    it&#91;intPreferencesKey(\"VOLUME\")]=value.toInt()\n                }\n            }\n        }\n\n\n    }\n\n\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Kotlin U11 02 Grabacion local\" width=\"1200\" height=\"675\" src=\"https:\/\/www.youtube.com\/embed\/-EKP7JIZ_OY?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p><strong>Lectura de la informaci\u00f3n<\/strong><\/p>\n\n\n\n<p>Ser\u00e1 necesario realizar la lectura y guardar la informaci\u00f3n en los controles. Hay que tener la precauci\u00f3n de los controles muy din\u00e1micos que la lectura y grabaci\u00f3n de los valores har\u00e1 inestable el control, con lo que hay que poner una variable para controlar que solo lo haga una vez.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.javi.u11v\n\nimport android.content.Context\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport android.util.Log\nimport android.widget.Switch\nimport androidx.appcompat.app.AppCompatDelegate\nimport androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO\nimport androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.booleanPreferencesKey\nimport androidx.datastore.preferences.core.edit\nimport androidx.datastore.preferences.core.intPreferencesKey\nimport androidx.datastore.preferences.preferencesDataStore\nimport com.google.android.material.slider.RangeSlider\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.collect\nimport kotlinx.coroutines.flow.filter\nimport kotlinx.coroutines.flow.map\nimport kotlinx.coroutines.launch\n\nval Context.dataStore: DataStore&lt;Preferences&gt; by preferencesDataStore(\"settings\")\n<strong>var firstTime=true<\/strong>\n\nclass MainActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n\n        val switchDarkMod=findViewById&lt;Switch&gt;(R.id.switchDarkMod)\n        val rsVolume = findViewById&lt;RangeSlider&gt;(R.id.rsVolume)\n\n<strong>        \/\/LECTURA\n        CoroutineScope(Dispatchers.IO).launch {\n            val darkMode: Flow&lt;Boolean&gt; = dataStore.data.map{\n                it&#91;booleanPreferencesKey(\"OSCURE_MODE\")]?:false\n            }\n            darkMode.collect{\n                runOnUiThread{\n                    switchDarkMod.isChecked=it\n                }\n            }\n        }\n\n        CoroutineScope(Dispatchers.IO).launch {\n            val volume: Flow&lt;Int&gt; = dataStore.data.map{\n                it&#91;intPreferencesKey(\"VOLUME\")]?:50\n            }\n            volume.filter{firstTime}.collect{\n                runOnUiThread{\n                    rsVolume.setValues(it.toFloat())\n                    firstTime=!firstTime\n                }\n            }\n        }<\/strong>\n\n\n\n        \/\/GRABACI\u00d3N\n        switchDarkMod.setOnCheckedChangeListener { compoundButton, b -&gt;\n            Log.i(\"JAI\",\"El valor es $b\")\n\n\n            CoroutineScope(Dispatchers.IO).launch {\n                dataStore.edit {\n                    it&#91;booleanPreferencesKey(\"OSCURE_MODE\")]=b\n                }\n            }\n        }\n\n        rsVolume.addOnChangeListener { slider, value, fromUser -&gt;\n            Log.i(\"JAI\",\"El volumen es $value\")\n            CoroutineScope(Dispatchers.IO).launch {\n                dataStore.edit {\n                    it&#91;intPreferencesKey(\"VOLUME\")]=value.toInt()\n                }\n            }\n        }\n\n\n    }\n\n\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Kotlin U11 03 Lectura local\" width=\"1200\" height=\"675\" src=\"https:\/\/www.youtube.com\/embed\/ooP2nKVwvVc?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p><strong>Modo oscuro<\/strong><\/p>\n\n\n\n<p>Vemos como podemos activar y desactivar el modo oscuro en el momento de activar y desactivar el control<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package com.javi.u11v\n\nimport android.content.Context\nimport androidx.appcompat.app.AppCompatActivity\nimport android.os.Bundle\nimport android.util.Log\nimport android.widget.Switch\nimport androidx.appcompat.app.AppCompatDelegate\nimport androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_NO\nimport androidx.appcompat.app.AppCompatDelegate.MODE_NIGHT_YES\nimport androidx.datastore.core.DataStore\nimport androidx.datastore.preferences.core.Preferences\nimport androidx.datastore.preferences.core.booleanPreferencesKey\nimport androidx.datastore.preferences.core.edit\nimport androidx.datastore.preferences.core.intPreferencesKey\nimport androidx.datastore.preferences.preferencesDataStore\nimport com.google.android.material.slider.RangeSlider\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.flow.Flow\nimport kotlinx.coroutines.flow.collect\nimport kotlinx.coroutines.flow.filter\nimport kotlinx.coroutines.flow.map\nimport kotlinx.coroutines.launch\n\nval Context.dataStore: DataStore&lt;Preferences&gt; by preferencesDataStore(\"settings\")\nvar firstTime=true\n\nclass MainActivity : AppCompatActivity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n\n        val switchDarkMod=findViewById&lt;Switch&gt;(R.id.switchDarkMod)\n        val rsVolume = findViewById&lt;RangeSlider&gt;(R.id.rsVolume)\n\n        \/\/LECTURA\n        CoroutineScope(Dispatchers.IO).launch {\n            val darkMode: Flow&lt;Boolean&gt; = dataStore.data.map{\n                it&#91;booleanPreferencesKey(\"OSCURE_MODE\")]?:false\n            }\n            darkMode.collect{\n                runOnUiThread{\n                    switchDarkMod.isChecked=it\n<strong>                    if (it) enableDarkMode()\n                    else disableDarkMode()<\/strong>\n                }\n            }\n        }\n\n        CoroutineScope(Dispatchers.IO).launch {\n            val volume: Flow&lt;Int&gt; = dataStore.data.map{\n                it&#91;intPreferencesKey(\"VOLUME\")]?:50\n            }\n            volume.filter{firstTime}.collect{\n                runOnUiThread{\n                    rsVolume.setValues(it.toFloat())\n                    firstTime=!firstTime\n                }\n            }\n        }\n\n\n\n        \/\/GRABACI\u00d3N\n        switchDarkMod.setOnCheckedChangeListener { compoundButton, b -&gt;\n            Log.i(\"JAI\",\"El valor es $b\")\n<strong>            if (b) enableDarkMode()\n            else disableDarkMode()<\/strong>\n\n            CoroutineScope(Dispatchers.IO).launch {\n                dataStore.edit {\n                    it&#91;booleanPreferencesKey(\"OSCURE_MODE\")]=b\n                }\n            }\n        }\n\n        rsVolume.addOnChangeListener { slider, value, fromUser -&gt;\n            Log.i(\"JAI\",\"El volumen es $value\")\n            CoroutineScope(Dispatchers.IO).launch {\n                dataStore.edit {\n                    it&#91;intPreferencesKey(\"VOLUME\")]=value.toInt()\n                }\n            }\n        }\n\n\n    }\n\n<strong>    private fun disableDarkMode() {\n        AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_NO)\n        delegate.applyDayNight()\n    }\n\n    private fun enableDarkMode() {\n        AppCompatDelegate.setDefaultNightMode(MODE_NIGHT_YES)\n        delegate.applyDayNight()\n    }<\/strong>\n}<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-embed is-type-video is-provider-youtube wp-block-embed-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio\"><div class=\"wp-block-embed__wrapper\">\n<iframe loading=\"lazy\" title=\"Kotlin U11 04 Modo Oscuro\" width=\"1200\" height=\"675\" src=\"https:\/\/www.youtube.com\/embed\/-EJlJrXZ8bM?feature=oembed\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/div><\/figure>\n\n\n\n<p><em>Informaci\u00f3n recopila de Aristidevs<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pantalla de configuraci\u00f3n Lo primero que se realizar es la creaci\u00f3n de la pantalla de configuraci\u00f3n. Grabaci\u00f3n de la informaci\u00f3n Es necesario modificar el gradel &hellip; <\/p>\n","protected":false},"author":1,"featured_media":575,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[29,32,91,110,111,212],"class_list":["post-574","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-kotlin","tag-almacenamiento-local","tag-android-studio","tag-configuracion","tag-datastorage","tag-datastore","tag-kotlin"],"_links":{"self":[{"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/posts\/574","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/comments?post=574"}],"version-history":[{"count":1,"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/posts\/574\/revisions"}],"predecessor-version":[{"id":908,"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/posts\/574\/revisions\/908"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/media\/575"}],"wp:attachment":[{"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/media?parent=574"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/categories?post=574"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/javigomez.org\/index.php\/wp-json\/wp\/v2\/tags?post=574"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}