home: add music loading error dialog
Add a dialog that shows the stack trace of a music loading error. This is an MVP that is only available to music loading to resolve some immediate issues. Resolves #527.
This commit is contained in:
parent
ada29b2f7a
commit
f5c7f25cdf
13 changed files with 294 additions and 85 deletions
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Auxio Project
|
||||
* ErrorDetailsDialog.kt is part of Auxio.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.oxycblt.auxio.home
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.databinding.DialogErrorDetailsBinding
|
||||
import org.oxycblt.auxio.ui.ViewBindingMaterialDialogFragment
|
||||
import org.oxycblt.auxio.util.getSystemServiceCompat
|
||||
import org.oxycblt.auxio.util.openInBrowser
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
|
||||
/**
|
||||
* A dialog that shows a stack trace for a music loading error.
|
||||
*
|
||||
* TODO: Extend to other errors
|
||||
*
|
||||
* @author Alexander Capehart (OxygenCobalt)
|
||||
*/
|
||||
class ErrorDetailsDialog : ViewBindingMaterialDialogFragment<DialogErrorDetailsBinding>() {
|
||||
private val args: ErrorDetailsDialogArgs by navArgs()
|
||||
private var clipboardManager: ClipboardManager? = null
|
||||
|
||||
override fun onConfigDialog(builder: AlertDialog.Builder) {
|
||||
builder
|
||||
.setTitle(R.string.lbl_error_info)
|
||||
.setPositiveButton(R.string.lbl_report) { _, _ ->
|
||||
requireContext().openInBrowser(LINK_ISSUES)
|
||||
}
|
||||
.setNegativeButton(R.string.lbl_cancel, null)
|
||||
}
|
||||
|
||||
override fun onCreateBinding(inflater: LayoutInflater) =
|
||||
DialogErrorDetailsBinding.inflate(inflater)
|
||||
|
||||
override fun onBindingCreated(binding: DialogErrorDetailsBinding, savedInstanceState: Bundle?) {
|
||||
super.onBindingCreated(binding, savedInstanceState)
|
||||
|
||||
clipboardManager = requireContext().getSystemServiceCompat(ClipboardManager::class)
|
||||
|
||||
// --- UI SETUP ---
|
||||
binding.errorStackTrace.text = args.error.stackTraceToString().trimEnd('\n')
|
||||
binding.errorCopy.setOnClickListener { copyStackTrace() }
|
||||
}
|
||||
|
||||
override fun onDestroyBinding(binding: DialogErrorDetailsBinding) {
|
||||
super.onDestroyBinding(binding)
|
||||
clipboardManager = null
|
||||
}
|
||||
|
||||
private fun copyStackTrace() {
|
||||
requireNotNull(clipboardManager) { "Clipboard was unavailable" }
|
||||
.setPrimaryClip(
|
||||
ClipData.newPlainText("Exception Stack Trace", args.error.stackTraceToString()))
|
||||
// A copy notice is shown by the system from Android 13 onwards
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
requireContext().showToast(R.string.lbl_copied)
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
/** The URL to the bug report issue form */
|
||||
const val LINK_ISSUES =
|
||||
"https://github.com/OxygenCobalt/Auxio/issues/new" +
|
||||
"?assignees=OxygenCobalt&labels=bug&projects=&template=bug-crash-report.yml"
|
||||
}
|
||||
}
|
|
@ -330,11 +330,12 @@ class HomeFragment :
|
|||
}
|
||||
}
|
||||
|
||||
private fun setupCompleteState(binding: FragmentHomeBinding, error: Throwable?) {
|
||||
private fun setupCompleteState(binding: FragmentHomeBinding, error: Exception?) {
|
||||
if (error == null) {
|
||||
logD("Received ok response")
|
||||
binding.homeFab.show()
|
||||
binding.homeIndexingContainer.visibility = View.INVISIBLE
|
||||
binding.homeIndexingError.visibility = View.INVISIBLE
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -357,6 +358,7 @@ class HomeFragment :
|
|||
.launch(PERMISSION_READ_AUDIO)
|
||||
}
|
||||
}
|
||||
binding.homeIndexingError.visibility = View.INVISIBLE
|
||||
}
|
||||
is NoMusicException -> {
|
||||
logD("Showing no music error")
|
||||
|
@ -367,6 +369,7 @@ class HomeFragment :
|
|||
text = context.getString(R.string.lbl_retry)
|
||||
setOnClickListener { musicModel.refresh() }
|
||||
}
|
||||
binding.homeIndexingError.visibility = View.INVISIBLE
|
||||
}
|
||||
else -> {
|
||||
logD("Showing generic error")
|
||||
|
@ -377,6 +380,12 @@ class HomeFragment :
|
|||
text = context.getString(R.string.lbl_retry)
|
||||
setOnClickListener { musicModel.rescan() }
|
||||
}
|
||||
binding.homeIndexingError.apply {
|
||||
visibility = View.VISIBLE
|
||||
setOnClickListener {
|
||||
findNavController().navigateSafe(HomeFragmentDirections.reportError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ sealed interface IndexingState {
|
|||
* @param error If music loading has failed, the error that occurred will be here. Otherwise, it
|
||||
* will be null.
|
||||
*/
|
||||
data class Completed(val error: Throwable?) : IndexingState
|
||||
data class Completed(val error: Exception?) : IndexingState
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -371,6 +371,7 @@ constructor(
|
|||
// parallel.
|
||||
logD("Starting MediaStore query")
|
||||
emitIndexingProgress(IndexingProgress.Indeterminate)
|
||||
|
||||
val mediaStoreQueryJob =
|
||||
worker.scope.async {
|
||||
val query =
|
||||
|
|
|
@ -18,13 +18,8 @@
|
|||
|
||||
package org.oxycblt.auxio.settings
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.navigation.fragment.findNavController
|
||||
|
@ -37,8 +32,7 @@ import org.oxycblt.auxio.music.MusicViewModel
|
|||
import org.oxycblt.auxio.playback.formatDurationMs
|
||||
import org.oxycblt.auxio.ui.ViewBindingFragment
|
||||
import org.oxycblt.auxio.util.collectImmediately
|
||||
import org.oxycblt.auxio.util.logD
|
||||
import org.oxycblt.auxio.util.showToast
|
||||
import org.oxycblt.auxio.util.openInBrowser
|
||||
import org.oxycblt.auxio.util.systemBarInsetsCompat
|
||||
|
||||
/**
|
||||
|
@ -69,10 +63,10 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
|||
}
|
||||
|
||||
binding.aboutVersion.text = BuildConfig.VERSION_NAME
|
||||
binding.aboutCode.setOnClickListener { openLinkInBrowser(LINK_SOURCE) }
|
||||
binding.aboutWiki.setOnClickListener { openLinkInBrowser(LINK_WIKI) }
|
||||
binding.aboutLicenses.setOnClickListener { openLinkInBrowser(LINK_LICENSES) }
|
||||
binding.aboutAuthor.setOnClickListener { openLinkInBrowser(LINK_AUTHOR) }
|
||||
binding.aboutCode.setOnClickListener { requireContext().openInBrowser(LINK_SOURCE) }
|
||||
binding.aboutWiki.setOnClickListener { requireContext().openInBrowser(LINK_WIKI) }
|
||||
binding.aboutLicenses.setOnClickListener { requireContext().openInBrowser(LINK_LICENSES) }
|
||||
binding.aboutAuthor.setOnClickListener { requireContext().openInBrowser(LINK_AUTHOR) }
|
||||
|
||||
// VIEWMODEL SETUP
|
||||
collectImmediately(musicModel.statistics, ::updateStatistics)
|
||||
|
@ -93,74 +87,6 @@ class AboutFragment : ViewBindingFragment<FragmentAboutBinding>() {
|
|||
(statistics?.durationMs ?: 0).formatDurationMs(false))
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the given URI in a web browser.
|
||||
*
|
||||
* @param uri The URL to open.
|
||||
*/
|
||||
private fun openLinkInBrowser(uri: String) {
|
||||
logD("Opening $uri")
|
||||
val context = requireContext()
|
||||
val browserIntent =
|
||||
Intent(Intent.ACTION_VIEW, uri.toUri()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Android 11 seems to now handle the app chooser situations on its own now
|
||||
// [along with adding a new permission that breaks the old manual code], so
|
||||
// we just do a typical activity launch.
|
||||
logD("Using API 30+ chooser")
|
||||
try {
|
||||
context.startActivity(browserIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// No app installed to open the link
|
||||
context.showToast(R.string.err_no_app)
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
// On older versions of android, opening links from an ACTION_VIEW intent might
|
||||
// not work in all cases, especially when no default app was set. If that is the
|
||||
// case, we will try to manually handle these cases before we try to launch the
|
||||
// browser.
|
||||
logD("Resolving browser activity for chooser")
|
||||
val pkgName =
|
||||
context.packageManager
|
||||
.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)
|
||||
?.run { activityInfo.packageName }
|
||||
|
||||
if (pkgName != null) {
|
||||
if (pkgName == "android") {
|
||||
// No default browser [Must open app chooser, may not be supported]
|
||||
logD("No default browser found")
|
||||
openAppChooser(browserIntent)
|
||||
} else logD("Opening browser intent")
|
||||
try {
|
||||
browserIntent.setPackage(pkgName)
|
||||
startActivity(browserIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// Not a browser but an app chooser
|
||||
browserIntent.setPackage(null)
|
||||
openAppChooser(browserIntent)
|
||||
}
|
||||
} else {
|
||||
// No app installed to open the link
|
||||
context.showToast(R.string.err_no_app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Open an app chooser for a given [Intent].
|
||||
*
|
||||
* @param intent The [Intent] to show an app chooser for.
|
||||
*/
|
||||
private fun openAppChooser(intent: Intent) {
|
||||
logD("Opening app chooser for ${intent.action}")
|
||||
val chooserIntent =
|
||||
Intent(Intent.ACTION_CHOOSER)
|
||||
.putExtra(Intent.EXTRA_INTENT, intent)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(chooserIntent)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
/** The URL to the source code. */
|
||||
const val LINK_SOURCE = "https://github.com/OxygenCobalt/Auxio"
|
||||
|
|
|
@ -18,7 +18,10 @@
|
|||
|
||||
package org.oxycblt.auxio.util
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.PointF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
|
@ -33,6 +36,7 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
|
|||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.graphics.Insets
|
||||
import androidx.core.graphics.drawable.DrawableCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.children
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDirections
|
||||
|
@ -41,6 +45,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.viewbinding.ViewBinding
|
||||
import com.google.android.material.appbar.MaterialToolbar
|
||||
import java.lang.IllegalArgumentException
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.music.MusicParent
|
||||
import org.oxycblt.auxio.music.Song
|
||||
|
||||
|
@ -322,3 +327,65 @@ fun Context.share(songs: Collection<Song>) {
|
|||
|
||||
builder.setType(mimeTypes.singleOrNull() ?: "audio/*").startChooser()
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the given URI in a web browser.
|
||||
*
|
||||
* @param uri The URL to open.
|
||||
*/
|
||||
fun Context.openInBrowser(uri: String) {
|
||||
fun openAppChooser(intent: Intent) {
|
||||
logD("Opening app chooser for ${intent.action}")
|
||||
val chooserIntent =
|
||||
Intent(Intent.ACTION_CHOOSER)
|
||||
.putExtra(Intent.EXTRA_INTENT, intent)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(chooserIntent)
|
||||
}
|
||||
|
||||
logD("Opening $uri")
|
||||
val browserIntent =
|
||||
Intent(Intent.ACTION_VIEW, uri.toUri()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// Android 11 seems to now handle the app chooser situations on its own now
|
||||
// [along with adding a new permission that breaks the old manual code], so
|
||||
// we just do a typical activity launch.
|
||||
logD("Using API 30+ chooser")
|
||||
try {
|
||||
startActivity(browserIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// No app installed to open the link
|
||||
showToast(R.string.err_no_app)
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
// On older versions of android, opening links from an ACTION_VIEW intent might
|
||||
// not work in all cases, especially when no default app was set. If that is the
|
||||
// case, we will try to manually handle these cases before we try to launch the
|
||||
// browser.
|
||||
logD("Resolving browser activity for chooser")
|
||||
val pkgName =
|
||||
packageManager.resolveActivity(browserIntent, PackageManager.MATCH_DEFAULT_ONLY)?.run {
|
||||
activityInfo.packageName
|
||||
}
|
||||
|
||||
if (pkgName != null) {
|
||||
if (pkgName == "android") {
|
||||
// No default browser [Must open app chooser, may not be supported]
|
||||
logD("No default browser found")
|
||||
openAppChooser(browserIntent)
|
||||
} else logD("Opening browser intent")
|
||||
try {
|
||||
browserIntent.setPackage(pkgName)
|
||||
startActivity(browserIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// Not a browser but an app chooser
|
||||
browserIntent.setPackage(null)
|
||||
openAppChooser(browserIntent)
|
||||
}
|
||||
} else {
|
||||
// No app installed to open the link
|
||||
showToast(R.string.err_no_app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
11
app/src/main/res/drawable/ic_copy_24.xml
Normal file
11
app/src/main/res/drawable/ic_copy_24.xml
Normal 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="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M200,880Q167,880 143.5,856.5Q120,833 120,800L120,240L200,240L200,800Q200,800 200,800Q200,800 200,800L640,800L640,880L200,880ZM360,720Q327,720 303.5,696.5Q280,673 280,640L280,160Q280,127 303.5,103.5Q327,80 360,80L720,80Q753,80 776.5,103.5Q800,127 800,160L800,640Q800,673 776.5,696.5Q753,720 720,720L360,720ZM360,640L720,640Q720,640 720,640Q720,640 720,640L720,160Q720,160 720,160Q720,160 720,160L360,160Q360,160 360,160Q360,160 360,160L360,640Q360,640 360,640Q360,640 360,640ZM360,640Q360,640 360,640Q360,640 360,640L360,160Q360,160 360,160Q360,160 360,160L360,160Q360,160 360,160Q360,160 360,160L360,640Q360,640 360,640Q360,640 360,640L360,640Z"/>
|
||||
</vector>
|
|
@ -69,8 +69,8 @@
|
|||
android:id="@+id/playback_seek_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/spacing_small"
|
||||
android:layout_marginEnd="@dimen/spacing_small"
|
||||
android:layout_marginStart="@dimen/spacing_tiny"
|
||||
android:layout_marginEnd="@dimen/spacing_tiny"
|
||||
app:layout_constraintBottom_toTopOf="@+id/playback_controls_container"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
|
|
67
app/src/main/res/layout/dialog_error_details.xml
Normal file
67
app/src/main/res/layout/dialog_error_details.xml
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="@dimen/spacing_medium"
|
||||
android:paddingEnd="@dimen/spacing_large"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/error_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
style="@style/Widget.Material3.CardView.Filled"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="none">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/error_stack_trace"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/spacing_medium"
|
||||
android:paddingStart="@dimen/spacing_medium"
|
||||
android:paddingBottom="@dimen/spacing_medium"
|
||||
android:paddingEnd="@dimen/size_copy_button"
|
||||
android:breakStrategy="simple"
|
||||
android:hyphenationFrequency="none"
|
||||
android:typeface="monospace"
|
||||
tools:text="Stack trace here" />
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/error_copy"
|
||||
style="@style/Widget.Auxio.Button.Icon.Small"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
app:icon="@drawable/ic_copy_24"
|
||||
android:layout_margin="@dimen/spacing_small"
|
||||
app:backgroundTint="?attr/colorPrimaryContainer"
|
||||
android:src="@drawable/ic_code_24" />
|
||||
|
||||
</FrameLayout>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -108,16 +108,33 @@
|
|||
|
||||
<org.oxycblt.auxio.ui.RippleFixMaterialButton
|
||||
android:id="@+id/home_indexing_action"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/spacing_medium"
|
||||
android:layout_marginEnd="@dimen/spacing_medium"
|
||||
android:layout_marginEnd="@dimen/spacing_small"
|
||||
android:layout_marginBottom="@dimen/spacing_medium"
|
||||
android:text="@string/lbl_retry"
|
||||
style="@style/Widget.Auxio.Button.Secondary"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/home_indexing_error"
|
||||
app:layout_constraintTop_toBottomOf="@+id/home_indexing_status" />
|
||||
|
||||
<org.oxycblt.auxio.ui.RippleFixMaterialButton
|
||||
android:id="@+id/home_indexing_error"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/spacing_small"
|
||||
android:layout_marginEnd="@dimen/spacing_medium"
|
||||
android:text="@string/lbl_show_error_info"
|
||||
style="@style/Widget.Auxio.Button.Primary"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/home_indexing_action"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/home_indexing_action"
|
||||
app:layout_constraintTop_toTopOf="@+id/home_indexing_action" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
|
|
@ -81,8 +81,21 @@
|
|||
<action
|
||||
android:id="@+id/play_from_genre"
|
||||
app:destination="@id/play_from_genre_dialog" />
|
||||
<action
|
||||
android:id="@+id/report_error"
|
||||
app:destination="@id/error_details_dialog" />
|
||||
</fragment>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/error_details_dialog"
|
||||
android:name="org.oxycblt.auxio.home.ErrorDetailsDialog"
|
||||
android:label="error_details_dialog"
|
||||
tools:layout="@layout/dialog_error_details">
|
||||
<argument
|
||||
android:name="error"
|
||||
app:argType="java.lang.Exception" />
|
||||
</dialog>
|
||||
|
||||
<dialog
|
||||
android:id="@+id/song_sort_dialog"
|
||||
android:name="org.oxycblt.auxio.home.sort.SongSortDialog"
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
<dimen name="size_btn">48dp</dimen>
|
||||
<dimen name="size_accent_item">56dp</dimen>
|
||||
<dimen name="size_bottom_sheet_bar">64dp</dimen>
|
||||
<dimen name="size_copy_button">64dp</dimen>
|
||||
<dimen name="size_play_pause_button">72dp</dimen>
|
||||
|
||||
<dimen name="size_icon_small">24dp</dimen>
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
<string name="lbl_observing">Monitoring music library</string>
|
||||
<!-- As in to retry loading music -->
|
||||
<string name="lbl_retry">Retry</string>
|
||||
<!-- As in to show additional information about a music loading error -->
|
||||
<string name="lbl_show_error_info">More</string>
|
||||
<!-- As in grant permission -->
|
||||
<string name="lbl_grant">Grant</string>
|
||||
|
||||
|
@ -168,6 +170,12 @@
|
|||
|
||||
<string name="lbl_selection">Selection</string>
|
||||
|
||||
<string name="lbl_error_info">Error information</string>
|
||||
<!-- As in copied to the system clipboard -->
|
||||
<string name="lbl_copied">Copied</string>
|
||||
<!-- As in to report an error -->
|
||||
<string name="lbl_report">Report</string>
|
||||
|
||||
<!-- Long Namespace | Longer Descriptions -->
|
||||
<eat-comment />
|
||||
|
||||
|
|
Loading…
Reference in a new issue