Skip to content

Commit

Permalink
Merge branch 'feature/android-tv-settings' into develop
Browse files Browse the repository at this point in the history
# Conflicts:
#	app/src/main/res/raw/changelog.md
  • Loading branch information
cemrich committed Feb 4, 2024
2 parents bea9707 + ac3f92e commit e2bf221
Show file tree
Hide file tree
Showing 16 changed files with 274 additions and 93 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,5 @@ dependencies {
// android tv
implementation 'androidx.leanback:leanback:1.1.0-rc02'
implementation "androidx.leanback:leanback-tab:1.1.0-beta01"
implementation "androidx.leanback:leanback-preference:1.2.0-alpha02"
}
5 changes: 5 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@
android:screenOrientation="landscape"
android:theme="@style/LeanbackAppTheme" />

<activity
android:name=".tv.settings.SettingsActivity"
android:screenOrientation="landscape"
android:theme="@style/LeanbackAppTheme.Transparent" />

<activity
android:name=".tv.faq.FaqActivity"
android:screenOrientation="landscape"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,105 +1,34 @@
package de.christinecoenen.code.zapp.app.settings.ui

import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.navigation.fragment.findNavController
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.SwitchPreferenceCompat
import com.google.android.material.color.DynamicColors
import com.jakewharton.processphoenix.ProcessPhoenix
import androidx.preference.PreferenceFragmentCompat
import de.christinecoenen.code.zapp.R
import de.christinecoenen.code.zapp.app.settings.helper.ShortcutPreference
import de.christinecoenen.code.zapp.app.settings.repository.SettingsRepository
import de.christinecoenen.code.zapp.utils.system.LanguageHelper

import de.christinecoenen.code.zapp.utils.system.PreferenceFragmentHelper
import org.koin.android.ext.android.inject

class SettingsFragment : BaseSettingsFragment() {

companion object {

private const val PREF_SHORTCUTS = "pref_shortcuts"
private const val PREF_DYNAMIC_COLORS = "dynamic_colors"
private const val PREF_UI_MODE = "pref_ui_mode"
private const val PREF_LANGUAGE = "pref_key_language"
private const val PREF_CHANNEL_SELECTION = "pref_key_channel_selection"

}

private lateinit var settingsRepository: SettingsRepository
private lateinit var shortcutPreference: ShortcutPreference
private lateinit var dynamicColorsPreference: SwitchPreferenceCompat
private lateinit var uiModePreference: ListPreference
private lateinit var languagePreference: ListPreference
private lateinit var channelSelectionPreference: Preference
private val settingsRepository: SettingsRepository by inject()
private val preferenceFragmentHelper = PreferenceFragmentHelper(this, settingsRepository)

private val uiModeChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val uiMode = settingsRepository.prefValueToUiMode(newValue as String?)
AppCompatDelegate.setDefaultNightMode(uiMode)
private val channelSelectionClickListener = Preference.OnPreferenceClickListener {
val direction =
SettingsFragmentDirections.toChannelSelectionFragment()
findNavController().navigate(direction)
true
}

private val languageChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val languageTag = newValue as String
val appLocale = LocaleListCompat.forLanguageTags(languageTag)
AppCompatDelegate.setApplicationLocales(appLocale)

true
}

override fun onAttach(context: Context) {
super.onAttach(context)

settingsRepository = SettingsRepository(context)
}

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.preferences)

shortcutPreference = preferenceScreen.findPreference(PREF_SHORTCUTS)!!
dynamicColorsPreference = preferenceScreen.findPreference(PREF_DYNAMIC_COLORS)!!
uiModePreference = preferenceScreen.findPreference(PREF_UI_MODE)!!
languagePreference = preferenceScreen.findPreference(PREF_LANGUAGE)!!
channelSelectionPreference = preferenceScreen.findPreference(PREF_CHANNEL_SELECTION)!!

val languages = LanguageHelper.getAvailableLanguages(requireContext())
languagePreference.value = LanguageHelper.getCurrentLanguageTag()
languagePreference.entries = languages.values.toTypedArray()
languagePreference.entryValues = languages.keys.toTypedArray()

// only show the preference for dynamic colors when available (Android 12 and up)
dynamicColorsPreference.isVisible = DynamicColors.isDynamicColorAvailable()
dynamicColorsPreference.setOnPreferenceChangeListener { _, useDynamicColors ->
// save explicitly to persist before app restart
settingsRepository.dynamicColors = useDynamicColors as Boolean
// app restart
ProcessPhoenix.triggerRebirth(context)
true
}

channelSelectionPreference.setOnPreferenceClickListener {
val direction =
SettingsFragmentDirections.toChannelSelectionFragment()
findNavController().navigate(direction)
true
}
preferenceFragmentHelper.initPreferences(channelSelectionClickListener)
}

override fun onResume() {
super.onResume()

shortcutPreference.onPreferenceChangeListener = shortcutPreference
uiModePreference.onPreferenceChangeListener = uiModeChangeListener
languagePreference.onPreferenceChangeListener = languageChangeListener
}

override fun onPause() {
super.onPause()

shortcutPreference.onPreferenceChangeListener = null
uiModePreference.onPreferenceChangeListener = null
languagePreference.onPreferenceChangeListener = null
override fun onDestroy() {
super.onDestroy()
preferenceFragmentHelper.destroy()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class AboutFragment : Fragment(), AboutItemListener {
val binding = TvFragmentAboutBinding.inflate(inflater, container, false)

binding.grid.adapter = AboutListAdapter(this)
binding.grid.layoutManager = GridLayoutManager(requireContext(), 2)
binding.grid.layoutManager = GridLayoutManager(requireContext(), 3)

return binding.root
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ import de.christinecoenen.code.zapp.R
import de.christinecoenen.code.zapp.databinding.TvAboutItemBinding
import de.christinecoenen.code.zapp.tv.changelog.ChangelogActivity
import de.christinecoenen.code.zapp.tv.faq.FaqActivity
import de.christinecoenen.code.zapp.tv.settings.SettingsActivity

class AboutListAdapter(
private val listener: AboutItemListener
) : RecyclerView.Adapter<AboutViewViewHolder>() {

private val aboutItems = listOf(
AboutItem(
R.string.activity_settings_title,
R.drawable.ic_outline_settings_24,
SettingsActivity
),
AboutItem(
R.string.changelog_title,
R.drawable.ic_sharp_format_list_bulleted_24,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import de.christinecoenen.code.zapp.R
import de.christinecoenen.code.zapp.app.settings.repository.SettingsRepository
import de.christinecoenen.code.zapp.databinding.TvFragmentMainBinding
import org.koin.android.ext.android.inject


class MainFragment : Fragment() {

private var _binding: TvFragmentMainBinding? = null
private val binding: TvFragmentMainBinding get() = _binding!!

private val settingsRepository: SettingsRepository by inject()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand All @@ -23,7 +28,13 @@ class MainFragment : Fragment() {
binding.viewpager.adapter = MainNavPagerAdapter(requireContext(), parentFragmentManager)
binding.tabs.setupWithViewPager(binding.viewpager)

binding.tabs.getTabAt(0)?.view?.requestFocus()
val selectedTabIndex = when (settingsRepository.startFragment) {
R.id.mediathekListFragment -> 1
else -> 0
}
val selectedTab = binding.tabs.getTabAt(selectedTabIndex)
binding.tabs.selectTab(selectedTab)
selectedTab?.view?.requestFocus()

return binding.root
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package de.christinecoenen.code.zapp.tv.settings

import android.os.Bundle
import androidx.leanback.preference.LeanbackPreferenceFragmentCompat
import de.christinecoenen.code.zapp.R
import de.christinecoenen.code.zapp.app.settings.repository.SettingsRepository
import de.christinecoenen.code.zapp.utils.system.PreferenceFragmentHelper
import org.koin.android.ext.android.inject

class PreferenceFragment : LeanbackPreferenceFragmentCompat() {

private val settingsRepository: SettingsRepository by inject()
private val preferenceFragmentHelper = PreferenceFragmentHelper(this, settingsRepository)

override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.tv_preferences, rootKey)

preferenceFragmentHelper.initPreferences()
}

override fun onDestroy() {
super.onDestroy()
preferenceFragmentHelper.destroy()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package de.christinecoenen.code.zapp.tv.settings

import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import de.christinecoenen.code.zapp.databinding.TvActivitySettingsBinding
import de.christinecoenen.code.zapp.utils.system.IStartableActivity

class SettingsActivity : FragmentActivity() {

companion object : IStartableActivity {
override fun getStartIntent(context: Context?): Intent =
Intent(context, SettingsActivity::class.java)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val binding = TvActivitySettingsBinding.inflate(layoutInflater)

setContentView(binding.root)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package de.christinecoenen.code.zapp.tv.settings

import androidx.leanback.preference.LeanbackSettingsFragmentCompat
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen


class SettingsFragment : LeanbackSettingsFragmentCompat() {

override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat,
pref: Preference
): Boolean {
return false
}

override fun onPreferenceStartScreen(
caller: PreferenceFragmentCompat,
pref: PreferenceScreen
): Boolean {
return false
}

override fun onPreferenceStartInitialScreen() {
startPreferenceFragment(PreferenceFragment())
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package de.christinecoenen.code.zapp.utils.system

import androidx.appcompat.app.AppCompatDelegate
import androidx.core.os.LocaleListCompat
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.preference.ListPreference
import androidx.preference.Preference
import androidx.preference.Preference.OnPreferenceClickListener
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat
import com.google.android.material.color.DynamicColors
import com.jakewharton.processphoenix.ProcessPhoenix
import de.christinecoenen.code.zapp.app.settings.helper.ShortcutPreference
import de.christinecoenen.code.zapp.app.settings.repository.SettingsRepository

class PreferenceFragmentHelper(
private val preferenceFragment: PreferenceFragmentCompat,
private val settingsRepository: SettingsRepository,
) : DefaultLifecycleObserver {

companion object {

private const val PREF_SHORTCUTS = "pref_shortcuts"
private const val PREF_DYNAMIC_COLORS = "dynamic_colors"
private const val PREF_UI_MODE = "pref_ui_mode"
private const val PREF_LANGUAGE = "pref_key_language"
private const val PREF_CHANNEL_SELECTION = "pref_key_channel_selection"

}

private var shortcutPreference: ShortcutPreference? = null
private var dynamicColorsPreference: SwitchPreferenceCompat? = null
private var uiModePreference: ListPreference? = null
private var languagePreference: ListPreference? = null
private var channelSelectionPreference: Preference? = null

private var channelSelectionClickListener: OnPreferenceClickListener? = null

private val dynamicColorChangeListener = Preference.OnPreferenceChangeListener { _, _ ->
ProcessPhoenix.triggerRebirth(preferenceFragment.requireContext())
true
}

private val uiModeChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val uiMode = settingsRepository.prefValueToUiMode(newValue as String?)
AppCompatDelegate.setDefaultNightMode(uiMode)
true
}

private val languageChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
val languageTag = newValue as String
val appLocale = LocaleListCompat.forLanguageTags(languageTag)
AppCompatDelegate.setApplicationLocales(appLocale)
true
}

init {
preferenceFragment.lifecycle.addObserver(this)
}

fun initPreferences(channelSelectionClickListener: OnPreferenceClickListener? = null) {
val preferenceScreen = preferenceFragment.preferenceScreen

shortcutPreference = preferenceScreen.findPreference(PREF_SHORTCUTS)
dynamicColorsPreference = preferenceScreen.findPreference(PREF_DYNAMIC_COLORS)
uiModePreference = preferenceScreen.findPreference(PREF_UI_MODE)
languagePreference = preferenceScreen.findPreference(PREF_LANGUAGE)
channelSelectionPreference = preferenceScreen.findPreference(PREF_CHANNEL_SELECTION)

languagePreference?.let {
val languages =
LanguageHelper.getAvailableLanguages(preferenceFragment.requireContext())
it.value = LanguageHelper.getCurrentLanguageTag()
it.entries = languages.values.toTypedArray()
it.entryValues = languages.keys.toTypedArray()
}

// only show the preference for dynamic colors when available (Android 12 and up)
dynamicColorsPreference?.let {
it.isVisible = DynamicColors.isDynamicColorAvailable()
}

this.channelSelectionClickListener = channelSelectionClickListener
}

fun destroy() {
preferenceFragment.lifecycle.removeObserver(this)
}

override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)

shortcutPreference?.onPreferenceChangeListener = shortcutPreference
dynamicColorsPreference?.onPreferenceChangeListener = dynamicColorChangeListener
uiModePreference?.onPreferenceChangeListener = uiModeChangeListener
languagePreference?.onPreferenceChangeListener = languageChangeListener
channelSelectionPreference?.onPreferenceClickListener = channelSelectionClickListener
}

override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)

shortcutPreference?.onPreferenceChangeListener = null
dynamicColorsPreference?.onPreferenceChangeListener = null
uiModePreference?.onPreferenceChangeListener = null
languagePreference?.onPreferenceChangeListener = null
channelSelectionPreference?.onPreferenceClickListener = null
}
}
Loading

0 comments on commit e2bf221

Please sign in to comment.