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
implementation 'com.afollestad.material-dialogs:core:3.3.0'
// Edge-To-Edge
implementation 'de.halfbit:edge-to-edge:0.10'
// Edge-To-Edge insets
implementation "dev.chrisbanes:insetter-ktx:0.3.1"
// --- DEV ---

View file

@ -1,21 +1,34 @@
package org.oxycblt.auxio
import android.content.Context
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.WindowInsets
import androidx.appcompat.app.AppCompatActivity
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.settings.SettingsManager
import org.oxycblt.auxio.ui.accent
import org.oxycblt.auxio.ui.handleTransparentSystemBars
// FIXME: Fix bug where fast navigation will break the animations and
// lead to nothing being displayed [Possibly Un-fixable]
// TODO: Landscape UI layouts
// FIXME: Compat issue with Versions 5 that leads to progress bar looking off
class MainActivity : AppCompatActivity(R.layout.activity_main) {
override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? {
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(
this, R.layout.activity_main
)
val settingsManager = SettingsManager.init(applicationContext)
AppCompatDelegate.setDefaultNightMode(
@ -27,7 +40,41 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) {
// Apply the theme
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() {
@ -41,8 +88,4 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) {
fun doThemeRecreate(newTheme: Int) {
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)
iconView = thumbView.findViewById(R.id.fast_scroller_thumb_icon)
isActivated = false
isVisible = false
applyStyle()
thumbAnimation = SpringAnimation(thumbView, DynamicAnimation.TRANSLATION_Y).apply {
@ -87,10 +90,12 @@ class NoLeakThumbView @JvmOverloads constructor(
)
) {
isActivated = false
isVisible = true
return@setOnTouchListener true
}
isActivated = isPointerOnItem(fastScrollerView, event.y.toInt())
isVisible = true
true
}

View file

@ -29,8 +29,7 @@ class SettingsListFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.apply {
}
// --- PREFERENCE ITEM SETUP ---
val themePref = findPreference<Preference>(SettingsManager.Keys.KEY_THEME)?.apply {
setIcon(
@ -53,6 +52,8 @@ class SettingsListFragment : PreferenceFragmentCompat() {
summary = getDetailedAccentSummary(requireActivity(), accent)
}
// --- VIEWMODEL SETUP ---
settingsModel.theme.observe(viewLifecycleOwner) {
if (it != null) {
themePref?.setIcon(
@ -75,12 +76,20 @@ class SettingsListFragment : PreferenceFragmentCompat() {
if (it != null) {
accentPref?.summary = getDetailedAccentSummary(requireActivity(), it)
(requireActivity() as MainActivity).doAccentRecreate()
requireActivity().recreate()
settingsModel.doneWithAccentUpdate()
}
}
settingsModel.edge.observe(viewLifecycleOwner) {
if (it != null) {
requireActivity().recreate()
settingsModel.doneWithEdgeUpdate()
}
}
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) {
sharedPrefs.edit()
.putInt(Keys.KEY_LIBRARY_SORT_MODE, sortMode.toInt())
@ -80,10 +84,6 @@ class SettingsManager private constructor(context: Context) :
) ?: SortMode.ALPHA_DOWN
}
fun getEdgeToEdge(): Boolean {
return sharedPrefs.getBoolean(Keys.KEY_EDGE_TO_EDGE, false)
}
// --- OVERRIDES ---
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
@ -93,6 +93,12 @@ class SettingsManager private constructor(context: Context) :
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>?>()
val accent: LiveData<Pair<Int, Int>?> get() = mAccent
private val mEdge = MutableLiveData<Boolean?>()
val edge: LiveData<Boolean?> = mEdge
private val settingsManager = SettingsManager.getInstance()
init {
@ -25,18 +28,22 @@ class SettingsViewModel : ViewModel(), SettingsManager.Callback {
mAccent.value = null
}
override fun onThemeUpdate(newTheme: Int) {
super.onThemeUpdate(newTheme)
fun doneWithEdgeUpdate() {
mEdge.value = null
}
override fun onThemeUpdate(newTheme: Int) {
mTheme.value = newTheme
}
override fun onAccentUpdate(newAccent: Pair<Int, Int>) {
super.onAccentUpdate(newAccent)
mAccent.value = newAccent
}
override fun onEdgeToEdgeUpdate(isEdgeToEdge: Boolean) {
mEdge.value = isEdgeToEdge
}
override fun onCleared() {
super.onCleared()

View file

@ -2,14 +2,20 @@ package org.oxycblt.auxio.ui
import android.content.Context
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.os.Build
import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.MenuItem
import android.view.View
import android.view.Window
import android.view.WindowInsetsController
import android.widget.ImageButton
import android.widget.Toast
import androidx.annotation.ColorInt
import androidx.annotation.MenuRes
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.PopupMenu
import androidx.core.text.HtmlCompat
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]
*/

View file

@ -1,11 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".MainActivity"
xmlns:app="http://schemas.android.com/apk/res-auto"
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
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:fitsSystemWindows">true</item>
<item name="android:scrollbars">none</item>
<item name="android:windowIsFloating">false</item>
<item name="popupMenuStyle">@style/Widget.CustomPopup</item>
<item name="colorControlNormal">@color/control_color</item>
@ -15,9 +16,7 @@
<item name="indicatorFastScrollerStyle">@style/FastScrollTheme</item>
<item name="colorControlActivated">?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_corner_radius">0dp</item>
<item name="md_color_button_text">@color/control_color</item>
@ -88,20 +87,9 @@
<item name="android:textSize">18sp</item>
</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">
<item name="android:windowIsFloating">false</item>
<item name="android:navigationBarColor">@color/background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
-->
</resources>