For #2389 - Create Theme Onboarding Card
parent
42d1491976
commit
d347f73e69
|
@ -6,9 +6,14 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
|
|||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.onboarding_section_header.view.*
|
||||
import kotlinx.android.synthetic.main.onboarding_theme_picker.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.asActivity
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
class OnboardingThemePickerViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
|
@ -19,4 +24,71 @@ class OnboardingThemePickerViewHolder(private val view: View) : RecyclerView.Vie
|
|||
companion object {
|
||||
const val LAYOUT_ID = R.layout.onboarding_theme_picker
|
||||
}
|
||||
|
||||
init {
|
||||
val radioLightTheme = view.theme_light_radio_button
|
||||
val radioDarkTheme = view.theme_dark_radio_button
|
||||
val radioFollowDeviceTheme = view.theme_automatic_radio_button
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
|
||||
radioFollowDeviceTheme?.key = R.string.pref_key_follow_device_theme
|
||||
} else {
|
||||
radioFollowDeviceTheme?.key = R.string.pref_key_auto_battery_theme
|
||||
}
|
||||
|
||||
radioLightTheme.addToRadioGroup(radioDarkTheme)
|
||||
radioDarkTheme.addToRadioGroup(radioLightTheme)
|
||||
|
||||
radioLightTheme.addToRadioGroup(radioFollowDeviceTheme)
|
||||
radioDarkTheme.addToRadioGroup(radioFollowDeviceTheme)
|
||||
|
||||
radioFollowDeviceTheme.addToRadioGroup(radioDarkTheme)
|
||||
radioFollowDeviceTheme.addToRadioGroup(radioLightTheme)
|
||||
|
||||
view.theme_dark_image.setOnClickListener {
|
||||
radioDarkTheme.performClick()
|
||||
}
|
||||
|
||||
view.theme_light_image.setOnClickListener {
|
||||
radioLightTheme.performClick()
|
||||
}
|
||||
|
||||
view.clickable_region_automatic.setOnClickListener {
|
||||
radioFollowDeviceTheme.performClick()
|
||||
}
|
||||
|
||||
radioLightTheme.onClickListener {
|
||||
setNewTheme(AppCompatDelegate.MODE_NIGHT_NO)
|
||||
}
|
||||
|
||||
radioDarkTheme.onClickListener {
|
||||
setNewTheme(AppCompatDelegate.MODE_NIGHT_YES)
|
||||
}
|
||||
|
||||
radioFollowDeviceTheme.onClickListener {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
|
||||
setNewTheme(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
} else {
|
||||
setNewTheme(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY)
|
||||
}
|
||||
}
|
||||
|
||||
with(Settings.getInstance(view.context)) {
|
||||
when {
|
||||
this.shouldFollowDeviceTheme -> radioFollowDeviceTheme.isChecked = true
|
||||
this.shouldUseLightTheme -> radioLightTheme.isChecked = true
|
||||
else -> radioDarkTheme.isChecked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setNewTheme(mode: Int) {
|
||||
if (AppCompatDelegate.getDefaultNightMode() == mode) return
|
||||
AppCompatDelegate.setDefaultNightMode(mode)
|
||||
view.context?.asActivity()?.recreate()
|
||||
view.context?.components?.core?.let {
|
||||
it.engine.settings.preferredColorScheme = it.getPreferredColorScheme()
|
||||
}
|
||||
view.context?.components?.useCases?.sessionUseCases?.reload?.invoke()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package org.mozilla.fenix.onboarding
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.widget.RadioButton
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
class OnboardingRadioButton : RadioButton {
|
||||
private val radioGroups = mutableListOf<OnboardingRadioButton>()
|
||||
private var clickListener: (() -> Unit)? = null
|
||||
var key: Int = 0
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
|
||||
attrs.let {
|
||||
context.theme.obtainStyledAttributes(
|
||||
it,
|
||||
R.styleable.OnboardingRadioButton,
|
||||
0, 0
|
||||
).apply {
|
||||
try {
|
||||
key = getResourceId(
|
||||
R.styleable.OnboardingRadioButton_onboardingKey, 0
|
||||
)
|
||||
} finally {
|
||||
recycle()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addToRadioGroup(radioButton: OnboardingRadioButton) {
|
||||
radioGroups.add(radioButton)
|
||||
}
|
||||
|
||||
fun onClickListener(listener: (() -> Unit)) {
|
||||
clickListener = listener
|
||||
}
|
||||
|
||||
init {
|
||||
setOnClickListener {
|
||||
updateRadioValue(true)
|
||||
toggleRadioGroups()
|
||||
clickListener?.invoke()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRadioValue(isChecked: Boolean) {
|
||||
this.isChecked = isChecked
|
||||
Settings.getInstance(context).preferences.edit().putBoolean(context.getString(key), isChecked)
|
||||
.apply()
|
||||
}
|
||||
|
||||
private fun toggleRadioGroups() {
|
||||
if (this.isChecked) {
|
||||
radioGroups.forEach { it.updateRadioValue(false) }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,17 +4,17 @@
|
|||
|
||||
package org.mozilla.fenix.settings
|
||||
|
||||
import android.view.View
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import android.widget.TextView
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.RadioButton
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.res.TypedArrayUtils
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
|
||||
|
@ -69,13 +69,13 @@ class RadioButtonPreference : Preference {
|
|||
private fun updateRadioValue(isChecked: Boolean) {
|
||||
persistBoolean(isChecked)
|
||||
radioButton.isChecked = isChecked
|
||||
Settings.getInstance(summaryView.context).preferences.edit().putBoolean(key, isChecked)
|
||||
Settings.getInstance(context).preferences.edit().putBoolean(key, isChecked)
|
||||
.apply()
|
||||
}
|
||||
|
||||
private fun bindRadioButton(holder: PreferenceViewHolder) {
|
||||
radioButton = holder.findViewById(R.id.radio_button) as RadioButton
|
||||
radioButton.isChecked = getPersistedBoolean(defaultValue)
|
||||
radioButton.isChecked = Settings.getInstance(context).preferences.getBoolean(key, false)
|
||||
}
|
||||
|
||||
private fun initDefaultValue(typedArray: TypedArray) {
|
||||
|
|
|
@ -98,6 +98,7 @@ class ThemeFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
|
||||
private fun setNewTheme(mode: Int) {
|
||||
if (AppCompatDelegate.getDefaultNightMode() == mode) return
|
||||
AppCompatDelegate.setDefaultNightMode(mode)
|
||||
activity?.recreate()
|
||||
with(requireComponents.core) {
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:attr/colorControlHighlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="#000000" />
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/tab_corner_radius"
|
||||
android:bottomRightRadius="@dimen/tab_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="?shadow" />
|
||||
<solid android:color="?inset" />
|
||||
<corners
|
||||
android:bottomLeftRadius="@dimen/tab_corner_radius"
|
||||
android:bottomRightRadius="@dimen/tab_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:attr/colorControlHighlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape>
|
||||
<solid android:color="#000000" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
|
@ -2,24 +2,168 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/onboarding_card"
|
||||
style="@style/OnboardingCardLight"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@drawable/onboarding_card_background_light"
|
||||
android:paddingTop="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/header_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="14dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="14dp"
|
||||
android:text="@string/onboarding_theme_picker_header"
|
||||
android:textAppearance="@style/HeaderTextStyle" />
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:text="@string/onboarding_theme_picker_description"
|
||||
android:textAppearance="@style/Body14TextStyle" />
|
||||
</LinearLayout>
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/header_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/light_theme_description"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/onboarding_theme_light_title"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="@+id/theme_light_image"
|
||||
app:layout_constraintStart_toStartOf="@id/theme_light_image"
|
||||
app:layout_constraintTop_toBottomOf="@id/theme_light_image" />
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/theme_light_radio_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="1dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/theme_light_image"
|
||||
app:layout_constraintCircle="@id/theme_light_image"
|
||||
app:layout_constraintCircleAngle="298"
|
||||
app:layout_constraintCircleRadius="66dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text"
|
||||
app:onboardingKey="@string/pref_key_light_theme" />
|
||||
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/theme_light_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/onboarding_light_theme"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
app:layout_constraintEnd_toStartOf="@+id/theme_dark_image"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text" />
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/theme_dark_radio_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="1dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/theme_dark_image"
|
||||
app:layout_constraintCircle="@id/theme_dark_image"
|
||||
app:layout_constraintCircleAngle="298"
|
||||
app:layout_constraintCircleRadius="66dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text"
|
||||
app:onboardingKey="@string/pref_key_dark_theme" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/theme_dark_image"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/onboarding_dark_theme"
|
||||
android:foreground="@drawable/rounded_ripple"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/theme_light_image"
|
||||
app:layout_constraintTop_toBottomOf="@+id/description_text" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dark_theme_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/onboarding_theme_dark_title"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="@+id/theme_dark_image"
|
||||
app:layout_constraintStart_toStartOf="@id/theme_dark_image"
|
||||
app:layout_constraintTop_toBottomOf="@id/theme_dark_image" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="1dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="1dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="?attr/neutralFaded"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/dark_theme_title" />
|
||||
|
||||
<View
|
||||
android:id="@+id/clickable_region_automatic"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/onboarding_rounded_bottom_corners_ripple"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/divider" />
|
||||
|
||||
<org.mozilla.fenix.onboarding.OnboardingRadioButton
|
||||
android:id="@+id/theme_automatic_radio_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/divider"
|
||||
app:onboardingKey="@string/pref_key_follow_device_theme" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/automatic_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="9dp"
|
||||
android:text="@string/onboarding_theme_automatic_title"
|
||||
android:textColor="?primaryText"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintStart_toEndOf="@+id/theme_automatic_radio_button"
|
||||
app:layout_constraintTop_toBottomOf="@+id/divider" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/automatic_theme_summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/onboarding_theme_automatic_summary"
|
||||
android:textColor="?secondaryText"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toEndOf="@+id/theme_automatic_radio_button"
|
||||
app:layout_constraintTop_toBottomOf="@id/automatic_title" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
|
@ -32,4 +32,8 @@
|
|||
<attr name="listItemTitle" format="reference" />
|
||||
<attr name="listItemIcon" format="reference" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="OnboardingRadioButton">
|
||||
<attr name="onboardingKey" format="reference" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
|
|
@ -488,4 +488,15 @@
|
|||
<string name="accessibility_text_size_sample_text">The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.</string>
|
||||
<string name="preference_accessibility_text_size_summary">Make text on websites larger or smaller</string>
|
||||
<string name="preference_accessibility_font_size_title">Font Size</string>
|
||||
|
||||
|
||||
<!-- Onboarding theme -->
|
||||
<!-- Automatic theme setting (will follow device setting) -->
|
||||
<string name="onboarding_theme_automatic_title">Automatic</string>
|
||||
<!-- Summary of automatic theme setting (will follow device setting) -->
|
||||
<string name="onboarding_theme_automatic_summary">Adapts to your device settings</string>
|
||||
<!-- Theme setting for dark mode -->
|
||||
<string name="onboarding_theme_dark_title">Dark Theme</string>
|
||||
<!-- Theme setting for light mode -->
|
||||
<string name="onboarding_theme_light_title">Light Theme</string>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue