Add about dialog

Add an about dialog with information about this app.
This commit is contained in:
OxygenCobalt 2020-12-18 10:05:31 -07:00
parent 4933531ca3
commit 0d32ab0f7e
No known key found for this signature in database
GPG key ID: 37DBE3621FE9AD47
29 changed files with 463 additions and 37 deletions

0
FAQ.md Normal file
View file

0
LIBRARIES.md Normal file
View file

0
LICENSES.md Normal file
View file

View file

@ -51,13 +51,14 @@ dependencies {
// Kotlin // Kotlin
//noinspection DifferentStdlibGradleVersion //noinspection DifferentStdlibGradleVersion
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
// --- SUPPORT --- // --- SUPPORT ---
// General // General
implementation 'androidx.core:core-ktx:1.3.2' implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.activity:activity-ktx:1.2.0-beta01' implementation 'androidx.activity:activity-ktx:1.2.0-beta02'
implementation 'androidx.fragment:fragment-ktx:1.3.0-beta01' implementation 'androidx.fragment:fragment-ktx:1.3.0-beta02'
// Layout // Layout
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
@ -79,6 +80,9 @@ dependencies {
// Preferences // Preferences
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.1.1'
// Opening links
implementation 'androidx.browser:browser:1.3.0'
// --- THIRD PARTY --- // --- THIRD PARTY ---
// ExoPlayer // ExoPlayer

View file

@ -7,6 +7,8 @@
<uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<queries />
<!-- TODO: Backup --> <!-- TODO: Backup -->
<application <application
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"

View file

@ -19,6 +19,7 @@ import org.oxycblt.auxio.ui.toColor
// 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]
// 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
// TODO: Try to heavily refactor edge-to-edge
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)

View file

@ -90,13 +90,8 @@ class CompactPlaybackFragment : Fragment() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
playbackModel.disableAnimation() playbackModel.disableAnimation()
// Use the caveman method of getting a view as storing the binding will cause a memory
// leak.
val playbackControls = binding.playbackControls
val iconPauseToPlay = ContextCompat.getDrawable( val iconPauseToPlay = ContextCompat.getDrawable(
requireContext(), R.drawable.ic_pause_to_play requireContext(), R.drawable.ic_pause_to_play
) as AnimatedVectorDrawable ) as AnimatedVectorDrawable
@ -109,19 +104,19 @@ class CompactPlaybackFragment : Fragment() {
if (playbackModel.canAnimate) { if (playbackModel.canAnimate) {
if (it) { if (it) {
// Animate the icon transition when the playing status switches // Animate the icon transition when the playing status switches
playbackControls?.setImageDrawable(iconPlayToPause) binding.playbackControls.setImageDrawable(iconPlayToPause)
iconPlayToPause.start() iconPlayToPause.start()
} else { } else {
playbackControls?.setImageDrawable(iconPauseToPlay) binding.playbackControls.setImageDrawable(iconPauseToPlay)
iconPauseToPlay.start() iconPauseToPlay.start()
} }
} else { } else {
// Use static icons on the first firing of this observer so that the icons // Use static icons on the first firing of this observer so that the icons
// don't animate on startup, which looks weird. // don't animate on startup, which looks weird.
if (it) { if (it) {
playbackControls?.setImageResource(R.drawable.ic_pause_large) binding.playbackControls.setImageResource(R.drawable.ic_pause_large)
} else { } else {
playbackControls?.setImageResource(R.drawable.ic_play_large) binding.playbackControls.setImageResource(R.drawable.ic_play_large)
} }
playbackModel.enableAnimation() playbackModel.enableAnimation()

View file

@ -28,7 +28,9 @@ import org.oxycblt.auxio.ui.toColor
*/ */
class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener { class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
private val playbackModel: PlaybackViewModel by activityViewModels() private val playbackModel: PlaybackViewModel by activityViewModels()
private val binding: FragmentPlaybackBinding by memberBinding(FragmentPlaybackBinding::inflate) private val binding: FragmentPlaybackBinding by memberBinding(FragmentPlaybackBinding::inflate) {
binding.playbackSong.isSelected = false
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
@ -199,12 +201,6 @@ class PlaybackFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
return binding.root return binding.root
} }
override fun onStop() {
super.onStop()
binding.playbackSong.isSelected = false
}
// Seeking callbacks // Seeking callbacks
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (fromUser) { if (fromUser) {

View file

@ -17,7 +17,8 @@ import org.oxycblt.auxio.music.BaseModel
import org.oxycblt.auxio.music.Header import org.oxycblt.auxio.music.Header
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.playback.state.PlaybackMode import org.oxycblt.auxio.playback.state.PlaybackMode
import org.oxycblt.auxio.ui.isInIrregularLandscapeMode import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.isIrregularLandscape
/** /**
* A [Fragment] that contains both the user queue and the next queue, with the ability to * A [Fragment] that contains both the user queue and the next queue, with the ability to
@ -49,7 +50,8 @@ class QueueFragment : Fragment() {
findNavController().navigateUp() findNavController().navigateUp()
} }
if (!requireActivity().isInIrregularLandscapeMode()) { if (!requireActivity().isIrregularLandscape() &&
SettingsManager.getInstance().edgeEnabled) {
setOnApplyWindowInsetsListener @Suppress("DEPRECATION") { _, insets -> setOnApplyWindowInsetsListener @Suppress("DEPRECATION") { _, insets ->
val top = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val top = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
insets.getInsets(WindowInsets.Type.systemBars()).top insets.getInsets(WindowInsets.Type.systemBars()).top

View file

@ -5,7 +5,9 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentSettingsBinding import org.oxycblt.auxio.databinding.FragmentSettingsBinding
import org.oxycblt.auxio.settings.ui.AboutDialog
class SettingsFragment : Fragment() { class SettingsFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
@ -15,6 +17,13 @@ class SettingsFragment : Fragment() {
): View { ): View {
val binding = FragmentSettingsBinding.inflate(inflater) val binding = FragmentSettingsBinding.inflate(inflater)
binding.settingsToolbar.setOnMenuItemClickListener {
if (it.itemId == R.id.action_open_about) {
AboutDialog().show(childFragmentManager, "DIALOG")
true
} else false
}
return binding.root return binding.root
} }
} }

View file

@ -17,7 +17,7 @@ import org.oxycblt.auxio.R
import org.oxycblt.auxio.logD import org.oxycblt.auxio.logD
import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.playback.PlaybackViewModel
import org.oxycblt.auxio.recycler.DisplayMode import org.oxycblt.auxio.recycler.DisplayMode
import org.oxycblt.auxio.settings.adapters.AccentAdapter import org.oxycblt.auxio.settings.ui.AccentAdapter
import org.oxycblt.auxio.ui.ACCENTS import org.oxycblt.auxio.ui.ACCENTS
import org.oxycblt.auxio.ui.accent import org.oxycblt.auxio.ui.accent
import org.oxycblt.auxio.ui.createToast import org.oxycblt.auxio.ui.createToast

View file

@ -0,0 +1,121 @@
package org.oxycblt.auxio.settings.ui
import android.app.Dialog
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.net.toUri
import androidx.fragment.app.DialogFragment
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import org.oxycblt.auxio.BuildConfig
import org.oxycblt.auxio.R
import org.oxycblt.auxio.databinding.FragmentAboutBinding
import org.oxycblt.auxio.logD
import org.oxycblt.auxio.logE
import org.oxycblt.auxio.music.MusicStore
import org.oxycblt.auxio.settings.SettingsManager
import org.oxycblt.auxio.ui.createToast
import org.oxycblt.auxio.ui.handleTransparentSystemBars
import org.oxycblt.auxio.ui.handleTransparentSystemNavBar
import org.oxycblt.auxio.ui.isIrregularLandscape
import org.oxycblt.auxio.ui.isLandscape
import org.oxycblt.auxio.ui.toColor
import java.lang.Exception
class AboutDialog : BottomSheetDialogFragment() {
override fun getTheme() = R.style.Theme_BottomSheetFix
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = FragmentAboutBinding.inflate(layoutInflater)
val musicStore = MusicStore.getInstance()
val settingsManager = SettingsManager.getInstance()
// --- UI SETUP ---
// Apply edge-to-edge to the dialog if supported/enabled.
if (settingsManager.edgeEnabled && !requireActivity().isIrregularLandscape()) {
requireDialog().window?.apply {
navigationBarColor = R.color.background.toColor(requireContext())
handleTransparentSystemNavBar(resources.configuration)
}
}
binding.aboutVersion.text = BuildConfig.VERSION_NAME
binding.aboutCode.setOnClickListener { openLinkInBrowser(LINK_CODEBASE) }
binding.aboutFaq.setOnClickListener { openLinkInBrowser(LINK_FAQ) }
binding.aboutLicenses.setOnClickListener { openLinkInBrowser(LINK_LICENSES) }
binding.aboutSongCount.text = getString(
R.string.format_songs_loaded, musicStore.songs.size.toString()
)
binding.aboutAuthor.text = getString(
R.string.format_author, getString(R.string.author_oxycblt)
)
logD("Dialog created.")
return binding.root
}
private fun openLinkInBrowser(link: String) {
check(link in LINKS) { "Invalid link." }
try {
val tabsIntent = CustomTabsIntent.Builder()
.setShareState(CustomTabsIntent.SHARE_STATE_ON)
.setShowTitle(true)
.build()
val uri = link.toUri()
val manager = requireActivity().packageManager
val browserCandidates = manager.queryIntentActivities(tabsIntent.intent, 0)
// If there are candidates for this link, then launch it through that.
if (browserCandidates.size > 0) {
tabsIntent.launchUrl(requireActivity(), uri)
} else {
// If there are no candidates, then fallback to another list of browsers
val browserIntent = Intent(Intent.ACTION_VIEW, uri)
browserIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
val fallbackCandidates = manager.queryIntentActivities(browserIntent, 0)
// If there are candidates here, then launch those.
if (fallbackCandidates.size > 0) {
requireActivity().startActivity(browserIntent)
} else {
// Otherwise they don't have a browser on their phone, meaning they should
// just see an error.
getString(R.string.error_no_browser).createToast(requireContext())
}
}
} catch (e: Exception) {
logE("Browser intent launching failed [Probably android's fault]")
e.printStackTrace()
// Sometimes people have """""Browsers""""" on their phone according to android,
// but they actually don't so here's a fallback for that.
getString(R.string.error_no_browser).createToast(requireContext())
}
}
companion object {
// TODO: Change dev to master
private const val LINK_CODEBASE = "https://github.com/oxygencobalt/Auxio"
private const val LINK_FAQ = "$LINK_CODEBASE/blob/master/FAQ.md"
private const val LINK_LICENSES = "$LINK_CODEBASE/blob/master/LICENSES.md"
val LINKS = arrayOf(LINK_CODEBASE, LINK_FAQ, LINK_LICENSES)
}
}

View file

@ -1,4 +1,4 @@
package org.oxycblt.auxio.settings.adapters package org.oxycblt.auxio.settings.ui
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.view.LayoutInflater import android.view.LayoutInflater

View file

@ -22,9 +22,8 @@ import org.oxycblt.auxio.settings.SettingsManager
* TODO: Make edge-to-edge work in irregular mode * TODO: Make edge-to-edge work in irregular mode
* @return True if we are in the irregular landscape mode, false if not. * @return True if we are in the irregular landscape mode, false if not.
*/ */
fun Activity.isInIrregularLandscapeMode(): Boolean { fun Activity.isIrregularLandscape(): Boolean {
return SettingsManager.getInstance().edgeEnabled && return isLandscape(resources) &&
isLandscape(resources) &&
!isSystemBarOnBottom(this) !isSystemBarOnBottom(this)
} }
@ -93,3 +92,31 @@ fun Window.handleTransparentSystemBars(config: Configuration) {
} }
} }
} }
/**
* Handle only the transparent navigation bar.
*/
fun Window.handleTransparentSystemNavBar(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
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_NAVIGATION_BAR or
View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
}
}
}

View file

@ -16,20 +16,21 @@ import kotlin.reflect.KProperty
/** /**
* A delegate that creates a binding that can be used as a member variable without nullability or * A delegate that creates a binding that can be used as a member variable without nullability or
* memory leaks. * memory leaks.
* @param bindingFactory The ViewBinding inflation method that should be used
* @param onDestroy Any code that should be run when the binding is destroyed.
*/ */
fun <T : ViewBinding> Fragment.memberBinding( fun <T : ViewBinding> Fragment.memberBinding(
viewBindingFactory: (LayoutInflater) -> T, bindingFactory: (LayoutInflater) -> T, onDestroy: T.() -> Unit = {}
disposeEvents: T.() -> Unit = {} ) = FragmentBinderDelegate(this, bindingFactory, onDestroy)
) = FragmentBinderDelegate(this, viewBindingFactory, disposeEvents)
/** /**
* The delegate for the [binder] shortcut function. * The delegate for the [memberBinding] shortcut function.
* Adapted from KAHelpers (https://github.com/FunkyMuse/KAHelpers/tree/master/viewbinding) * Adapted from KAHelpers (https://github.com/FunkyMuse/KAHelpers/tree/master/viewbinding)
*/ */
class FragmentBinderDelegate<T : ViewBinding>( class FragmentBinderDelegate<T : ViewBinding>(
private val fragment: Fragment, private val fragment: Fragment,
private val binder: (LayoutInflater) -> T, private val inflate: (LayoutInflater) -> T,
private val disposeEvents: T.() -> Unit private val onDestroy: T.() -> Unit
) : ReadOnlyProperty<Fragment, T>, LifecycleObserver { ) : ReadOnlyProperty<Fragment, T>, LifecycleObserver {
private var fragmentBinding: T? = null private var fragmentBinding: T? = null
@ -46,8 +47,8 @@ class FragmentBinderDelegate<T : ViewBinding>(
override fun onCreate(owner: LifecycleOwner) { override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner) super.onCreate(owner)
viewLifecycleOwnerLiveData.observe(this@observeOwnerThroughCreation) { owner -> viewLifecycleOwnerLiveData.observe(this@observeOwnerThroughCreation) {
owner.viewOwner() it.viewOwner()
} }
} }
}) })
@ -69,13 +70,13 @@ class FragmentBinderDelegate<T : ViewBinding>(
throw IllegalStateException("Fragment views are destroyed.") throw IllegalStateException("Fragment views are destroyed.")
} }
return binder(LayoutInflater.from(thisRef.requireContext())).also { fragmentBinding = it } return inflate(LayoutInflater.from(thisRef.requireContext())).also { fragmentBinding = it }
} }
@Suppress("UNUSED") @Suppress("UNUSED")
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun dispose() { fun dispose() {
fragmentBinding?.disposeEvents() fragmentBinding?.onDestroy()
fragmentBinding = null fragmentBinding = null
} }
} }

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11,18h2v-2h-2v2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM12,6c-2.21,0 -4,1.79 -4,4h2c0,-1.1 0.9,-2 2,-2s2,0.9 2,2c0,2 -3,1.75 -3,5h2c0,-2.25 3,-2.5 3,-5 0,-2.21 -1.79,-4 -4,-4z"/>
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM21.41,6.34l-3.75,-3.75 -2.53,2.54 3.75,3.75 2.53,-2.54z"/>
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M9.4,16.6L4.8,12l4.6,-4.6L8,6l-6,6 6,6 1.4,-1.4zM14.6,16.6l4.6,-4.6 -4.6,-4.6L16,6l6,6 -6,6 -1.4,-1.4z"/>
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M4,6L2,6v16h16v-2L4,20L4,6zM22,2L6,2v16h16L22,2zM19,11L9,11L9,9h10v2zM15,15L9,15v-2h6v2zM19,7L9,7L9,5h10v2z"/>
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M4.01,2L4,22h16V8l-6,-6H4.01zM13,9V3.5L18.5,9H13z"/>
</vector>

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/>
</vector>

View file

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background"
android:theme="@style/Theme.Neutral">
<ImageView
android:id="@+id/about_auxio_icon"
android:layout_width="@dimen/size_app_icon"
android:layout_height="@dimen/size_app_icon"
android:layout_marginTop="@dimen/margin_medium"
android:contentDescription="@string/description_auxio_icon"
android:src="@mipmap/ic_launcher"
app:layout_constraintEnd_toStartOf="@+id/about_app_name"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/about_app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium"
android:fontFamily="@font/inter_semibold"
android:text="@string/app_name"
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
app:layout_constraintBottom_toBottomOf="@+id/about_auxio_icon"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/about_auxio_icon"
app:layout_constraintTop_toTopOf="@+id/about_auxio_icon" />
<TextView
android:id="@+id/about_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:text="@string/app_desc"
android:textAlignment="center"
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_auxio_icon" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/version_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="@dimen/padding_medium"
app:layout_constraintBottom_toTopOf="@+id/about_code"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_desc"
app:layout_constraintVertical_bias="0"
app:layout_constraintVertical_chainStyle="packed">
<ImageView
android:id="@+id/about_version_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/label_version"
android:src="@drawable/ic_version"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/about_version_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_medium"
android:text="@string/label_version"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
app:layout_constraintBottom_toTopOf="@+id/about_version"
app:layout_constraintStart_toEndOf="@+id/about_version_icon"
app:layout_constraintTop_toTopOf="@+id/about_version_icon" />
<TextView
android:id="@+id/about_version"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Caption"
app:layout_constraintBottom_toBottomOf="@+id/about_version_icon"
app:layout_constraintStart_toEndOf="@+id/about_version_icon"
app:layout_constraintTop_toBottomOf="@+id/about_version_title"
tools:text="0.0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
<TextView
android:id="@+id/about_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ui_ripple"
android:clickable="true"
android:drawablePadding="@dimen/padding_medium"
android:focusable="true"
android:padding="@dimen/padding_medium"
android:text="@string/label_code"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
app:drawableLeftCompat="@drawable/ic_code"
app:layout_constraintBottom_toTopOf="@+id/about_faq"
app:layout_constraintTop_toBottomOf="@+id/version_container" />
<TextView
android:id="@+id/about_licenses"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ui_ripple"
android:clickable="true"
android:drawablePadding="@dimen/padding_medium"
android:focusable="true"
android:padding="@dimen/padding_medium"
android:text="@string/label_licenses"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
app:drawableLeftCompat="@drawable/ic_license"
app:layout_constraintBottom_toTopOf="@+id/about_song_count"
app:layout_constraintTop_toBottomOf="@+id/about_faq" />
<TextView
android:id="@+id/about_faq"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ui_ripple"
android:clickable="true"
android:drawablePadding="@dimen/padding_medium"
android:focusable="true"
android:padding="@dimen/padding_medium"
android:text="@string/label_faq"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
app:drawableLeftCompat="@drawable/ic_about"
app:layout_constraintBottom_toTopOf="@+id/about_licenses"
app:layout_constraintTop_toBottomOf="@+id/about_code"
tools:layout_editor_absoluteX="0dp" />
<TextView
android:id="@+id/about_song_count"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/padding_medium"
android:padding="@dimen/padding_medium"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
app:drawableLeftCompat="@drawable/ic_song"
app:layout_constraintBottom_toTopOf="@+id/about_author"
app:layout_constraintTop_toBottomOf="@+id/about_licenses"
tools:text="Songs Loaded: 1616" />
<TextView
android:id="@+id/about_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/padding_medium"
android:padding="@dimen/padding_medium"
android:textAppearance="@style/TextAppearance.AppCompat.Subhead"
app:drawableLeftCompat="@drawable/ic_author"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/about_song_count"
tools:text="Created by OxygenCobalt" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -8,10 +8,11 @@
android:orientation="vertical"> android:orientation="vertical">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/song_toolbar" android:id="@+id/settings_toolbar"
style="@style/Toolbar.Style" style="@style/Toolbar.Style"
android:background="?android:attr/windowBackground" android:background="?android:attr/windowBackground"
android:elevation="@dimen/elevation_normal" android:elevation="@dimen/elevation_normal"
app:menu="@menu/menu_settings"
app:title="@string/setting_title" /> app:title="@string/setting_title" />
<androidx.fragment.app.FragmentContainerView <androidx.fragment.app.FragmentContainerView

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_open_about"
android:title="@string/label_about"
android:icon="@drawable/ic_about"
app:showAsAction="always" />
</menu>

View file

@ -34,6 +34,8 @@
<dimen name="size_play_pause">70dp</dimen> <dimen name="size_play_pause">70dp</dimen>
<dimen name="size_play_pause_compact">36dp</dimen> <dimen name="size_play_pause_compact">36dp</dimen>
<dimen name="size_app_icon">60dp</dimen>
<!-- Text Size Namespace | Text Sizes --> <!-- Text Size Namespace | Text Sizes -->
<dimen name="text_size_min">10sp</dimen> <dimen name="text_size_min">10sp</dimen>
<dimen name="text_size_increment">2sp</dimen> <dimen name="text_size_increment">2sp</dimen>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="author_oxycblt">OxygenCobalt</string>
</resources>

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name">Auxio</string> <string name="app_name">Auxio</string>
<string name="app_desc">A sensible and customizable music player for android.</string>
<!-- Label Namespace | Static Labels --> <!-- Label Namespace | Static Labels -->
<string name="label_retry">Retry</string> <string name="label_retry">Retry</string>
@ -36,6 +37,12 @@
<string name="label_channel">Music Playback</string> <string name="label_channel">Music Playback</string>
<string name="label_service_playback">The music playback service for Auxio.</string> <string name="label_service_playback">The music playback service for Auxio.</string>
<string name="label_about">About</string>
<string name="label_version">Version</string>
<string name="label_code">View on Github</string>
<string name="label_licenses">Licenses</string>
<string name="label_faq">FAQ</string>
<!-- Settings namespace | Settings-related labels --> <!-- Settings namespace | Settings-related labels -->
<string name="setting_title">Settings</string> <string name="setting_title">Settings</string>
<string name="setting_ui">Appearance</string> <string name="setting_ui">Appearance</string>
@ -94,6 +101,7 @@
<string name="error_no_music">No music found.</string> <string name="error_no_music">No music found.</string>
<string name="error_music_load_failed">Music loading failed.</string> <string name="error_music_load_failed">Music loading failed.</string>
<string name="error_no_perms">Permissions to read storage are needed.</string> <string name="error_no_perms">Permissions to read storage are needed.</string>
<string name="error_no_browser">Could not open link.</string>
<!-- Hint Namespace | EditText Hints --> <!-- Hint Namespace | EditText Hints -->
<string name="hint_search_library">Search Library…</string> <string name="hint_search_library">Search Library…</string>
@ -116,6 +124,8 @@
<string name="description_shuffle_on">Turn shuffle on</string> <string name="description_shuffle_on">Turn shuffle on</string>
<string name="description_shuffle_off">Turn shuffle off</string> <string name="description_shuffle_off">Turn shuffle off</string>
<string name="description_change_loop">Change Repeat Mode</string> <string name="description_change_loop">Change Repeat Mode</string>
<string name="description_auxio_icon">Auxio icon</string>
<string name="description_code">Code</string>
<!-- Placeholder Namespace | Placeholder values --> <!-- Placeholder Namespace | Placeholder values -->
<string name="placeholder_genre">Unknown Genre</string> <string name="placeholder_genre">Unknown Genre</string>
@ -151,6 +161,8 @@
<string name="format_double_counts">%1$s, %2$s</string> <string name="format_double_counts">%1$s, %2$s</string>
<string name="format_next_from">Next From: %s</string> <string name="format_next_from">Next From: %s</string>
<string name="format_accent_summary">&lt;b>%1$s&lt;/b>:&#160;%2$s</string> <string name="format_accent_summary">&lt;b>%1$s&lt;/b>:&#160;%2$s</string>
<string name="format_songs_loaded">Songs Loaded: %s</string>
<string name="format_author">Created by %s</string>
<plurals name="format_song_count"> <plurals name="format_song_count">
<item quantity="one">%s Song</item> <item quantity="one">%s Song</item>

View file

@ -167,4 +167,10 @@
<item name="android:paddingEnd">@dimen/padding_medium</item> <item name="android:paddingEnd">@dimen/padding_medium</item>
<item name="android:background">@drawable/ui_small_unbounded_ripple</item> <item name="android:background">@drawable/ui_small_unbounded_ripple</item>
</style> </style>
<!-- Bottomsheet style -->
<style name="Theme.BottomSheetFix" parent="@style/Theme.Design.BottomSheetDialog">
<item name="android:windowIsFloating">false</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources> </resources>

View file

@ -1,6 +1,6 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = "1.4.20" ext.kotlin_version = "1.4.21"
repositories { repositories {
google() google()