Locale: support locale change

This commit is contained in:
Benoit Marty 2020-05-14 02:14:12 +02:00
parent 9fed62e4a7
commit f45040c405
17 changed files with 444 additions and 50 deletions

View File

@ -2,7 +2,7 @@ Changes in RiotX 0.21.0 (2020-XX-XX)
=================================================== ===================================================
Features ✨: Features ✨:
- - Switch language support (#41)
Improvements 🙌: Improvements 🙌:
- Better connectivity lost indicator when airplane mode is on - Better connectivity lost indicator when airplane mode is on

View File

@ -61,8 +61,6 @@ import im.vector.riotx.features.login.LoginSplashFragment
import im.vector.riotx.features.login.LoginWaitForEmailFragment import im.vector.riotx.features.login.LoginWaitForEmailFragment
import im.vector.riotx.features.login.LoginWebFragment import im.vector.riotx.features.login.LoginWebFragment
import im.vector.riotx.features.login.terms.LoginTermsFragment import im.vector.riotx.features.login.terms.LoginTermsFragment
import im.vector.riotx.features.userdirectory.KnownUsersFragment
import im.vector.riotx.features.userdirectory.UserDirectoryFragment
import im.vector.riotx.features.qrcode.QrCodeScannerFragment import im.vector.riotx.features.qrcode.QrCodeScannerFragment
import im.vector.riotx.features.reactions.EmojiChooserFragment import im.vector.riotx.features.reactions.EmojiChooserFragment
import im.vector.riotx.features.reactions.EmojiSearchResultFragment import im.vector.riotx.features.reactions.EmojiSearchResultFragment
@ -92,9 +90,12 @@ import im.vector.riotx.features.settings.devtools.IncomingKeyRequestListFragment
import im.vector.riotx.features.settings.devtools.KeyRequestsFragment import im.vector.riotx.features.settings.devtools.KeyRequestsFragment
import im.vector.riotx.features.settings.devtools.OutgoingKeyRequestListFragment import im.vector.riotx.features.settings.devtools.OutgoingKeyRequestListFragment
import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
import im.vector.riotx.features.settings.locale.LocalePickerFragment
import im.vector.riotx.features.settings.push.PushGatewaysFragment import im.vector.riotx.features.settings.push.PushGatewaysFragment
import im.vector.riotx.features.share.IncomingShareFragment import im.vector.riotx.features.share.IncomingShareFragment
import im.vector.riotx.features.signout.soft.SoftLogoutFragment import im.vector.riotx.features.signout.soft.SoftLogoutFragment
import im.vector.riotx.features.userdirectory.KnownUsersFragment
import im.vector.riotx.features.userdirectory.UserDirectoryFragment
@Module @Module
interface FragmentModule { interface FragmentModule {
@ -109,6 +110,11 @@ interface FragmentModule {
@FragmentKey(RoomListFragment::class) @FragmentKey(RoomListFragment::class)
fun bindRoomListFragment(fragment: RoomListFragment): Fragment fun bindRoomListFragment(fragment: RoomListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(LocalePickerFragment::class)
fun bindLocalePickerFragment(fragment: LocalePickerFragment): Fragment
@Binds @Binds
@IntoMap @IntoMap
@FragmentKey(GroupListFragment::class) @FragmentKey(GroupListFragment::class)

View File

@ -16,6 +16,7 @@
package im.vector.riotx.core.extensions package im.vector.riotx.core.extensions
import android.app.Activity
import android.os.Parcelable import android.os.Parcelable
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction import androidx.fragment.app.FragmentTransaction
@ -59,3 +60,8 @@ fun <T : Fragment> VectorBaseActivity.addFragmentToBackstack(frameId: Int,
fun VectorBaseActivity.hideKeyboard() { fun VectorBaseActivity.hideKeyboard() {
currentFocus?.hideKeyboard() currentFocus?.hideKeyboard()
} }
fun Activity.restart() {
startActivity(intent)
finish()
}

View File

@ -30,7 +30,6 @@ import javax.inject.Inject
/** /**
* Handle locale configuration change, such as theme, font size and locale chosen by the user * Handle locale configuration change, such as theme, font size and locale chosen by the user
*/ */
class VectorConfiguration @Inject constructor(private val context: Context) { class VectorConfiguration @Inject constructor(private val context: Context) {
// TODO Import mLanguageReceiver From Riot? // TODO Import mLanguageReceiver From Riot?
@ -98,7 +97,6 @@ class VectorConfiguration @Inject constructor(private val context: Context) {
* *
* @param locale * @param locale
*/ */
// TODO Call from LanguagePickerActivity
fun updateApplicationLocale(locale: Locale) { fun updateApplicationLocale(locale: Locale) {
updateApplicationSettings(locale, FontScale.getFontScalePrefValue(context), ThemeUtils.getApplicationTheme(context)) updateApplicationSettings(locale, FontScale.getFontScalePrefValue(context), ThemeUtils.getApplicationTheme(context))
} }

View File

@ -19,9 +19,8 @@ package im.vector.riotx.features.settings
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import androidx.preference.PreferenceManager
import androidx.core.content.edit import androidx.core.content.edit
import im.vector.riotx.BuildConfig import androidx.preference.PreferenceManager
import im.vector.riotx.R import im.vector.riotx.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -75,7 +74,7 @@ object VectorLocale {
saveApplicationLocale(context, applicationLocale) saveApplicationLocale(context, applicationLocale)
} }
// init the known locales in background, using kotlin coroutines // init the known locales in background
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
initApplicationLocales(context) initApplicationLocales(context)
} }
@ -144,6 +143,7 @@ object VectorLocale {
} else { } else {
val resources = context.resources val resources = context.resources
val conf = resources.configuration val conf = resources.configuration
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
val savedLocale = conf.locale val savedLocale = conf.locale
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@ -235,10 +235,18 @@ object VectorLocale {
append(locale.getDisplayCountry(locale)) append(locale.getDisplayCountry(locale))
append(")") append(")")
} }
}
}
// In debug mode, also display information about the locale in the current locale. /**
if (BuildConfig.DEBUG) { * Information about the locale in the current locale
append("\n[") *
* @param locale the locale to get info from
* @return the string
*/
fun localeToLocalisedStringInfo(locale: Locale): String {
return buildString {
append("[")
append(locale.displayLanguage) append(locale.displayLanguage)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && locale.script != "Latn") { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && locale.script != "Latn") {
append(" - ") append(" - ")
@ -252,5 +260,4 @@ object VectorLocale {
append("]") append("]")
} }
} }
}
} }

View File

@ -18,7 +18,6 @@ package im.vector.riotx.features.settings
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.widget.CheckedTextView import android.widget.CheckedTextView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
@ -129,21 +128,6 @@ class VectorSettingsPreferencesFragment @Inject constructor(
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
REQUEST_LOCALE -> {
activity?.let {
startActivity(it.intent)
it.finish()
}
}
}
}
}
// ============================================================================================================== // ==============================================================================================================
// user interface management // user interface management
// ============================================================================================================== // ==============================================================================================================
@ -152,12 +136,6 @@ class VectorSettingsPreferencesFragment @Inject constructor(
// Selected language // Selected language
selectedLanguagePreference.summary = VectorLocale.localeToLocalisedString(VectorLocale.applicationLocale) selectedLanguagePreference.summary = VectorLocale.localeToLocalisedString(VectorLocale.applicationLocale)
selectedLanguagePreference.onPreferenceClickListener = Preference.OnPreferenceClickListener {
notImplemented()
// TODO startActivityForResult(LanguagePickerActivity.getIntent(activity), REQUEST_LOCALE)
true
}
// Text size // Text size
textSizePreference.summary = FontScale.getFontScaleDescription(activity!!) textSizePreference.summary = FontScale.getFontScaleDescription(activity!!)
@ -199,8 +177,4 @@ class VectorSettingsPreferencesFragment @Inject constructor(
} }
} }
} }
companion object {
private const val REQUEST_LOCALE = 777
}
} }

View File

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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.
*/
package im.vector.riotx.features.settings.locale
import android.widget.TextView
import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.ClickListener
import im.vector.riotx.core.epoxy.VectorEpoxyHolder
import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.core.epoxy.onClick
import im.vector.riotx.core.extensions.setTextOrHide
@EpoxyModelClass(layout = R.layout.item_locale)
abstract class LocaleItem : VectorEpoxyModel<LocaleItem.Holder>() {
@EpoxyAttribute var title: String? = null
@EpoxyAttribute var subtitle: String? = null
@EpoxyAttribute var clickListener: ClickListener? = null
override fun bind(holder: Holder) {
super.bind(holder)
holder.view.onClick { clickListener?.invoke() }
holder.titleView.setTextOrHide(title)
holder.subtitleView.setTextOrHide(subtitle)
}
class Holder : VectorEpoxyHolder() {
val titleView by bind<TextView>(R.id.localeTitle)
val subtitleView by bind<TextView>(R.id.localeSubtitle)
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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.
*/
package im.vector.riotx.features.settings.locale
import im.vector.riotx.core.platform.VectorViewModelAction
import java.util.Locale
sealed class LocalePickerAction : VectorViewModelAction {
data class SelectLocale(val locale: Locale) : LocalePickerAction()
}

View File

@ -0,0 +1,82 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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.
*/
package im.vector.riotx.features.settings.locale
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.noResultItem
import im.vector.riotx.core.epoxy.profiles.profileSectionItem
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.features.settings.VectorLocale
import im.vector.riotx.features.settings.VectorPreferences
import java.util.Locale
import javax.inject.Inject
class LocalePickerController @Inject constructor(
private val vectorPreferences: VectorPreferences,
private val stringProvider: StringProvider
) : TypedEpoxyController<LocalePickerViewState>() {
var listener: Listener? = null
@ExperimentalStdlibApi
override fun buildModels(data: LocalePickerViewState?) {
val list = data?.locales ?: return
if (list.isEmpty()) {
noResultItem {
id("noResult")
text(stringProvider.getString(R.string.no_result_placeholder))
}
} else {
profileSectionItem {
id("currentTitle")
title(stringProvider.getString(R.string.choose_locale_current_locale_title))
}
localeItem {
id(data.currentLocale.toString())
title(VectorLocale.localeToLocalisedString(data.currentLocale).capitalize(data.currentLocale))
if (vectorPreferences.developerMode()) {
subtitle(VectorLocale.localeToLocalisedStringInfo(data.currentLocale))
}
clickListener { listener?.onUseCurrentClicked() }
}
profileSectionItem {
id("otherTitle")
title(stringProvider.getString(R.string.choose_locale_other_locales_title))
}
list
.filter { it != data.currentLocale }
.sortedBy { VectorLocale.localeToLocalisedString(it).toLowerCase(it) }
.forEach {
localeItem {
id(it.toString())
title(VectorLocale.localeToLocalisedString(it).capitalize(it))
if (vectorPreferences.developerMode()) {
subtitle(VectorLocale.localeToLocalisedStringInfo(it))
}
clickListener { listener?.onLocaleClicked(it) }
}
}
}
}
interface Listener {
fun onUseCurrentClicked()
fun onLocaleClicked(locale: Locale)
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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.
*/
package im.vector.riotx.features.settings.locale
import android.os.Bundle
import android.view.View
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.extensions.restart
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_locale_picker.*
import java.util.Locale
import javax.inject.Inject
class LocalePickerFragment @Inject constructor(
private val viewModelFactory: LocalePickerViewModel.Factory,
private val controller: LocalePickerController
) : VectorBaseFragment(), LocalePickerViewModel.Factory by viewModelFactory, LocalePickerController.Listener {
private val viewModel: LocalePickerViewModel by fragmentViewModel()
override fun getLayoutResId() = R.layout.fragment_locale_picker
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
localeRecyclerView.configureWith(controller)
controller.listener = this
viewModel.observeViewEvents {
when (it) {
LocalePickerViewEvents.RestartActivity -> {
activity?.restart()
}
}.exhaustive
}
}
override fun onDestroyView() {
super.onDestroyView()
localeRecyclerView.cleanup()
controller.listener = null
}
override fun invalidate() = withState(viewModel) { state ->
controller.setData(state)
}
override fun onUseCurrentClicked() {
// Just close the fragment
parentFragmentManager.popBackStack()
}
override fun onLocaleClicked(locale: Locale) {
viewModel.handle(LocalePickerAction.SelectLocale(locale))
}
override fun onResume() {
super.onResume()
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.settings_select_language)
}
}

View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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.
*/
package im.vector.riotx.features.settings.locale
import im.vector.riotx.core.platform.VectorViewEvents
sealed class LocalePickerViewEvents : VectorViewEvents {
object RestartActivity : LocalePickerViewEvents()
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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.
*/
package im.vector.riotx.features.settings.locale
import com.airbnb.mvrx.ActivityViewModelContext
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.features.configuration.VectorConfiguration
import im.vector.riotx.features.settings.VectorLocale
class LocalePickerViewModel @AssistedInject constructor(
@Assisted initialState: LocalePickerViewState,
private val vectorConfiguration: VectorConfiguration
) : VectorViewModel<LocalePickerViewState, LocalePickerAction, LocalePickerViewEvents>(initialState) {
@AssistedInject.Factory
interface Factory {
fun create(initialState: LocalePickerViewState): LocalePickerViewModel
}
companion object : MvRxViewModelFactory<LocalePickerViewModel, LocalePickerViewState> {
override fun initialState(viewModelContext: ViewModelContext): LocalePickerViewState? {
return LocalePickerViewState(
locales = VectorLocale.supportedLocales
)
}
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: LocalePickerViewState): LocalePickerViewModel? {
val factory = when (viewModelContext) {
is FragmentViewModelContext -> viewModelContext.fragment as? Factory
is ActivityViewModelContext -> viewModelContext.activity as? Factory
}
return factory?.create(state) ?: error("You should let your activity/fragment implements Factory interface")
}
}
override fun handle(action: LocalePickerAction) {
when (action) {
is LocalePickerAction.SelectLocale -> handleSelectLocale(action)
}.exhaustive
}
private fun handleSelectLocale(action: LocalePickerAction.SelectLocale) {
vectorConfiguration.updateApplicationLocale(action.locale)
_viewEvents.post(LocalePickerViewEvents.RestartActivity)
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* 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.
*/
package im.vector.riotx.features.settings.locale
import com.airbnb.mvrx.MvRxState
import java.util.Locale
data class LocalePickerViewState(
val currentLocale: Locale = Locale.getDefault(),
val locales: List<Locale> = emptyList()
) : MvRxState

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/localeRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?riotx_background"
tools:listitem="@layout/item_locale" />

View File

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
android:foreground="?attr/selectableItemBackground"
android:minHeight="64dp">
<TextView
android:id="@+id/localeTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingEnd="@dimen/layout_horizontal_margin"
android:textColor="?riotx_text_primary"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@+id/localeSubtitle"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="English" />
<TextView
android:id="@+id/localeSubtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="@dimen/layout_horizontal_margin"
android:paddingEnd="@dimen/layout_horizontal_margin"
android:textColor="?riotx_text_secondary"
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/localeTitle"
tools:text="details"
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2393,4 +2393,7 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
</plurals> </plurals>
<string name="invite_users_to_room_failure">We could not invite users. Please check the users you want to invite and try again.</string> <string name="invite_users_to_room_failure">We could not invite users. Please check the users you want to invite and try again.</string>
<string name="choose_locale_current_locale_title">Current language</string>
<string name="choose_locale_other_locales_title">Other available languages</string>
</resources> </resources>

View File

@ -7,9 +7,10 @@
android:title="@string/settings_user_interface"> android:title="@string/settings_user_interface">
<im.vector.riotx.core.preference.VectorPreference <im.vector.riotx.core.preference.VectorPreference
android:dialogTitle="@string/settings_select_language"
android:key="SETTINGS_INTERFACE_LANGUAGE_PREFERENCE_KEY" android:key="SETTINGS_INTERFACE_LANGUAGE_PREFERENCE_KEY"
android:title="@string/settings_interface_language" /> android:persistent="false"
android:title="@string/settings_interface_language"
app:fragment="im.vector.riotx.features.settings.locale.LocalePickerFragment" />
<im.vector.riotx.core.preference.VectorListPreference <im.vector.riotx.core.preference.VectorListPreference
android:defaultValue="light" android:defaultValue="light"