Add edge-to-edge

Add [somewhat basic] edge-to-edge functionality for android O and up, along with the ability to enable/disable it.
This commit is contained in:
OxygenCobalt 2020-11-29 19:30:09 -07:00
parent 59036a2747
commit 12c14eeda6
9 changed files with 143 additions and 44 deletions

View file

@ -97,8 +97,8 @@ dependencies {
// Dialogs // Dialogs
implementation 'com.afollestad.material-dialogs:core:3.3.0' implementation 'com.afollestad.material-dialogs:core:3.3.0'
// Edge-To-Edge // Edge-To-Edge insets
implementation 'de.halfbit:edge-to-edge:0.10' implementation "dev.chrisbanes:insetter-ktx:0.3.1"
// --- DEV --- // --- DEV ---

View file

@ -1,21 +1,34 @@
package org.oxycblt.auxio package org.oxycblt.auxio
import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.AttributeSet import android.graphics.Color
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import android.view.WindowInsets
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.databinding.DataBindingUtil
import dev.chrisbanes.insetter.applySystemWindowInsetsToMargin
import org.oxycblt.auxio.databinding.ActivityMainBinding
import org.oxycblt.auxio.playback.PlaybackService import org.oxycblt.auxio.playback.PlaybackService
import org.oxycblt.auxio.settings.SettingsManager import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.accent
import org.oxycblt.auxio.ui.handleTransparentSystemBars
// FIXME: Fix bug where fast navigation will break the animations and // FIXME: Fix bug where fast navigation will break the animations and
// lead to nothing being displayed [Possibly Un-fixable] // lead to nothing being displayed [Possibly Un-fixable]
// TODO: Landscape UI layouts // TODO: Landscape UI layouts
// FIXME: Compat issue with Versions 5 that leads to progress bar looking off // FIXME: Compat issue with Versions 5 that leads to progress bar looking off
class MainActivity : AppCompatActivity(R.layout.activity_main) { class MainActivity : AppCompatActivity() {
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
this, R.layout.activity_main
)
val settingsManager = SettingsManager.init(applicationContext) val settingsManager = SettingsManager.init(applicationContext)
AppCompatDelegate.setDefaultNightMode( AppCompatDelegate.setDefaultNightMode(
@ -27,7 +40,41 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) {
// Apply the theme // Apply the theme
setTheme(accent.second) setTheme(accent.second)
return super.onCreateView(name, context, attrs) // If enabled and possible, go through a stupidly long & complicated process
// just to get edge-to-edge to work.
// TODO: Make the navigation bar fully transparent
if (settingsManager.getEdgeToEdge() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
window?.apply {
statusBarColor = Color.TRANSPARENT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Log.d(this::class.simpleName, "Doing R+ edge-to-edge.")
setDecorFitsSystemWindows(false)
binding.root.setOnApplyWindowInsetsListener { v, insets ->
WindowInsets.Builder()
.setInsets(
WindowInsets.Type.systemBars(),
insets.getInsetsIgnoringVisibility(WindowInsets.Type.systemBars())
)
.build()
}
} else {
Log.d(this::class.simpleName, "Doing deprec edge-to-edge.")
binding.root.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
}
navigationBarColor = Color.TRANSPARENT
handleTransparentSystemBars(resources.configuration)
// I barely know how insets work so here's another third party library
// that I think does things
binding.root.applySystemWindowInsetsToMargin(top = false, bottom = false)
}
}
} }
override fun onStart() { override fun onStart() {
@ -41,8 +88,4 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) {
fun doThemeRecreate(newTheme: Int) { fun doThemeRecreate(newTheme: Int) {
AppCompatDelegate.setDefaultNightMode(newTheme) AppCompatDelegate.setDefaultNightMode(newTheme)
} }
fun doAccentRecreate() {
recreate()
}
} }

View file

@ -59,6 +59,9 @@ class NoLeakThumbView @JvmOverloads constructor(
textView = thumbView.findViewById(R.id.fast_scroller_thumb_text) textView = thumbView.findViewById(R.id.fast_scroller_thumb_text)
iconView = thumbView.findViewById(R.id.fast_scroller_thumb_icon) iconView = thumbView.findViewById(R.id.fast_scroller_thumb_icon)
isActivated = false
isVisible = false
applyStyle() applyStyle()
thumbAnimation = SpringAnimation(thumbView, DynamicAnimation.TRANSLATION_Y).apply { thumbAnimation = SpringAnimation(thumbView, DynamicAnimation.TRANSLATION_Y).apply {
@ -87,10 +90,12 @@ class NoLeakThumbView @JvmOverloads constructor(
) )
) { ) {
isActivated = false isActivated = false
isVisible = true
return@setOnTouchListener true return@setOnTouchListener true
} }
isActivated = isPointerOnItem(fastScrollerView, event.y.toInt()) isActivated = isPointerOnItem(fastScrollerView, event.y.toInt())
isVisible = true
true true
} }

View file

@ -29,8 +29,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
view.apply { // --- PREFERENCE ITEM SETUP ---
}
val themePref = findPreference<Preference>(SettingsManager.Keys.KEY_THEME)?.apply { val themePref = findPreference<Preference>(SettingsManager.Keys.KEY_THEME)?.apply {
setIcon( setIcon(
@ -53,6 +52,8 @@ class SettingsListFragment : PreferenceFragmentCompat() {
summary = getDetailedAccentSummary(requireActivity(), accent) summary = getDetailedAccentSummary(requireActivity(), accent)
} }
// --- VIEWMODEL SETUP ---
settingsModel.theme.observe(viewLifecycleOwner) { settingsModel.theme.observe(viewLifecycleOwner) {
if (it != null) { if (it != null) {
themePref?.setIcon( themePref?.setIcon(
@ -75,12 +76,20 @@ class SettingsListFragment : PreferenceFragmentCompat() {
if (it != null) { if (it != null) {
accentPref?.summary = getDetailedAccentSummary(requireActivity(), it) accentPref?.summary = getDetailedAccentSummary(requireActivity(), it)
(requireActivity() as MainActivity).doAccentRecreate() requireActivity().recreate()
settingsModel.doneWithAccentUpdate() settingsModel.doneWithAccentUpdate()
} }
} }
settingsModel.edge.observe(viewLifecycleOwner) {
if (it != null) {
requireActivity().recreate()
settingsModel.doneWithEdgeUpdate()
}
}
Log.d(this::class.simpleName, "Fragment created.") Log.d(this::class.simpleName, "Fragment created.")
} }

View file

@ -65,6 +65,10 @@ class SettingsManager private constructor(context: Context) :
} }
} }
fun getEdgeToEdge(): Boolean {
return sharedPrefs.getBoolean(Keys.KEY_EDGE_TO_EDGE, false)
}
fun setLibrarySortMode(sortMode: SortMode) { fun setLibrarySortMode(sortMode: SortMode) {
sharedPrefs.edit() sharedPrefs.edit()
.putInt(Keys.KEY_LIBRARY_SORT_MODE, sortMode.toInt()) .putInt(Keys.KEY_LIBRARY_SORT_MODE, sortMode.toInt())
@ -80,10 +84,6 @@ class SettingsManager private constructor(context: Context) :
) ?: SortMode.ALPHA_DOWN ) ?: SortMode.ALPHA_DOWN
} }
fun getEdgeToEdge(): Boolean {
return sharedPrefs.getBoolean(Keys.KEY_EDGE_TO_EDGE, false)
}
// --- OVERRIDES --- // --- OVERRIDES ---
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
@ -93,6 +93,12 @@ class SettingsManager private constructor(context: Context) :
it.onThemeUpdate(getTheme()) it.onThemeUpdate(getTheme())
} }
} }
Keys.KEY_EDGE_TO_EDGE -> {
callbacks.forEach {
it.onEdgeToEdgeUpdate(getEdgeToEdge())
}
}
} }
} }

View file

@ -11,6 +11,9 @@ class SettingsViewModel : ViewModel(), SettingsManager.Callback {
private val mAccent = MutableLiveData<Pair<Int, Int>?>() private val mAccent = MutableLiveData<Pair<Int, Int>?>()
val accent: LiveData<Pair<Int, Int>?> get() = mAccent val accent: LiveData<Pair<Int, Int>?> get() = mAccent
private val mEdge = MutableLiveData<Boolean?>()
val edge: LiveData<Boolean?> = mEdge
private val settingsManager = SettingsManager.getInstance() private val settingsManager = SettingsManager.getInstance()
init { init {
@ -25,18 +28,22 @@ class SettingsViewModel : ViewModel(), SettingsManager.Callback {
mAccent.value = null mAccent.value = null
} }
override fun onThemeUpdate(newTheme: Int) { fun doneWithEdgeUpdate() {
super.onThemeUpdate(newTheme) mEdge.value = null
}
override fun onThemeUpdate(newTheme: Int) {
mTheme.value = newTheme mTheme.value = newTheme
} }
override fun onAccentUpdate(newAccent: Pair<Int, Int>) { override fun onAccentUpdate(newAccent: Pair<Int, Int>) {
super.onAccentUpdate(newAccent)
mAccent.value = newAccent mAccent.value = newAccent
} }
override fun onEdgeToEdgeUpdate(isEdgeToEdge: Boolean) {
mEdge.value = isEdgeToEdge
}
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()

View file

@ -2,14 +2,20 @@ package org.oxycblt.auxio.ui
import android.content.Context import android.content.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.content.res.Configuration
import android.os.Build
import android.text.SpannableString import android.text.SpannableString
import android.text.Spanned import android.text.Spanned
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import android.view.Window
import android.view.WindowInsetsController
import android.widget.ImageButton import android.widget.ImageButton
import android.widget.Toast import android.widget.Toast
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.MenuRes import androidx.annotation.MenuRes
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import org.oxycblt.auxio.R import org.oxycblt.auxio.R
@ -66,6 +72,38 @@ fun Spanned.render(): Spanned {
) )
} }
/**
* Handle transparent system bars on light mode. Adapted from Music Player GO
* (https://github.com/enricocid/Music-Player-GO)
*/
@RequiresApi(Build.VERSION_CODES.O_MR1)
@Suppress("DEPRECATION")
fun Window.handleTransparentSystemBars(config: Configuration) {
fun isNight() = config.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insetsController?.let { controller ->
val appearance = WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS or
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
val mask = if (isNight()) 0 else appearance
controller.setSystemBarsAppearance(appearance, mask)
}
} else {
val flags = decorView.systemUiVisibility
decorView.systemUiVisibility =
if (isNight()) {
flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() and
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR.inv()
} else {
flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR or
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
}
}
/** /**
* Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment] * Show actions for a song item, such as the ones found in [org.oxycblt.auxio.songs.SongsFragment]
*/ */

View file

@ -1,11 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host" <layout xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_main"
tools:context=".MainActivity"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" /> xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="androidx.navigation.fragment.NavHostFragment"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_main" />
</layout>

View file

@ -8,6 +8,7 @@
<item name="android:textCursorDrawable">@drawable/ui_cursor</item> <item name="android:textCursorDrawable">@drawable/ui_cursor</item>
<item name="android:fitsSystemWindows">true</item> <item name="android:fitsSystemWindows">true</item>
<item name="android:scrollbars">none</item> <item name="android:scrollbars">none</item>
<item name="android:windowIsFloating">false</item>
<item name="popupMenuStyle">@style/Widget.CustomPopup</item> <item name="popupMenuStyle">@style/Widget.CustomPopup</item>
<item name="colorControlNormal">@color/control_color</item> <item name="colorControlNormal">@color/control_color</item>
@ -15,9 +16,7 @@
<item name="indicatorFastScrollerStyle">@style/FastScrollTheme</item> <item name="indicatorFastScrollerStyle">@style/FastScrollTheme</item>
<item name="colorControlActivated">?attr/colorPrimary</item> <item name="colorControlActivated">?attr/colorPrimary</item>
<item name="colorControlHighlight">?attr/colorPrimary</item> <item name="colorControlHighlight">?attr/colorPrimary</item>
<item name="preferenceTheme">@style/Theme.Preference</item>
<item name="md_divider_color">@android:color/transparent</item>
<item name="md_background_color">@color/background</item> <item name="md_background_color">@color/background</item>
<item name="md_corner_radius">0dp</item> <item name="md_corner_radius">0dp</item>
<item name="md_color_button_text">@color/control_color</item> <item name="md_color_button_text">@color/control_color</item>
@ -88,20 +87,9 @@
<item name="android:textSize">18sp</item> <item name="android:textSize">18sp</item>
</style> </style>
<style name="Theme.Preference" parent="PreferenceThemeOverlay">
<item name="rippleColor">@color/selection_color</item>
<item name="itemRippleColor">@color/selection_color</item>
<item name="tabRippleColor">@color/selection_color</item>
</style>
<!--
Fix to get QueueFragment to not overlap the Status Bar or Navigation Bar [Currently unused but still here]
https://stackoverflow.com/a/57790787/14143986
<style name="Theme.BottomSheetFix" parent="@style/Theme.Design.BottomSheetDialog"> <style name="Theme.BottomSheetFix" parent="@style/Theme.Design.BottomSheetDialog">
<item name="android:windowIsFloating">false</item> <item name="android:windowIsFloating">false</item>
<item name="android:navigationBarColor">@color/background</item> <item name="android:navigationBarColor">@color/background</item>
<item name="android:statusBarColor">@android:color/transparent</item> <item name="android:statusBarColor">@android:color/transparent</item>
</style> </style>
-->
</resources> </resources>