about: fix link opening not working on android 11
Auxio would usually open links by trying to walk through the app chooser/default app situation themselves, mostly for compat purposes. This method was not only broken in Android 11 with the addition of the QUERY_ALL_PACKAGES permission, but it also seems to be made obsolete since the android system seems to handle the app choser pretty well in 11+. As a result, we replace the link opening code on that version with a plain startActivity call and keep the old compat code for older versions. Resolves #47.
This commit is contained in:
parent
792cc22583
commit
b9658c698b
4 changed files with 71 additions and 52 deletions
|
@ -20,8 +20,10 @@ package org.oxycblt.auxio.playback.queue
|
|||
|
||||
import android.graphics.Canvas
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import org.oxycblt.auxio.R
|
||||
import org.oxycblt.auxio.playback.PlaybackViewModel
|
||||
import kotlin.math.abs
|
||||
|
@ -32,15 +34,15 @@ import kotlin.math.sign
|
|||
/**
|
||||
* The Drag callback used by the queue recyclerview. Delivers updates to [PlaybackViewModel]
|
||||
* and [QueueAdapter] simultaneously.
|
||||
* @param onItemScroll Callback for when an item begins to scroll off-screen. Argument passed
|
||||
* is the dY value.
|
||||
* @author OxygenCobalt
|
||||
*/
|
||||
class QueueDragCallback(
|
||||
private val playbackModel: PlaybackViewModel,
|
||||
private val onItemScroll: (Int) -> Unit
|
||||
private val coordinator: CoordinatorLayout,
|
||||
private val appBar: AppBarLayout
|
||||
) : ItemTouchHelper.Callback() {
|
||||
private lateinit var queueAdapter: QueueAdapter
|
||||
private val tConsumed = IntArray(2)
|
||||
private var shouldLift = true
|
||||
|
||||
override fun getMovementFlags(
|
||||
|
@ -79,24 +81,18 @@ class QueueDragCallback(
|
|||
val result = clampedAbsVelocity * sign(viewSizeOutOfBounds.toDouble()).toInt()
|
||||
|
||||
recyclerView.post {
|
||||
onItemScroll(result)
|
||||
// CoordinatorLayout refuses to propagate a scroll event initiated by an item scroll,
|
||||
// so we do it ourselves.
|
||||
(appBar.layoutParams as CoordinatorLayout.LayoutParams).behavior
|
||||
?.onNestedPreScroll(
|
||||
coordinator, appBar, recyclerView,
|
||||
0, result, tConsumed, 0
|
||||
)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
return playbackModel.moveQueueDataItems(
|
||||
viewHolder.bindingAdapterPosition,
|
||||
target.bindingAdapterPosition,
|
||||
queueAdapter
|
||||
)
|
||||
}
|
||||
|
||||
override fun onChildDraw(
|
||||
c: Canvas,
|
||||
recyclerView: RecyclerView,
|
||||
|
@ -107,10 +103,12 @@ class QueueDragCallback(
|
|||
isCurrentlyActive: Boolean
|
||||
) {
|
||||
// The material design page on elevation has a cool example of draggable items elevating
|
||||
// themselves when being dragged. Too bad google doesn't provide a single utility to do
|
||||
// this in your own app :^). To emulate it, I check if this child is in a drag state and
|
||||
// themselves when being dragged. Too bad google's implementation of this doesn't even
|
||||
// work :^). To emulate it on my own, I check if this child is in a drag state and
|
||||
// then animate an elevation change. This animation also changes the background so that
|
||||
// the item will actually draw over.
|
||||
// TODO: Maybe restrict the item from being drawn over the recycler bounds?
|
||||
// Seems like its possible with enough UI magic
|
||||
|
||||
val view = viewHolder.itemView
|
||||
|
||||
|
@ -129,7 +127,7 @@ class QueueDragCallback(
|
|||
}
|
||||
|
||||
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
|
||||
// When an elevated item is done, we reset the elevation using another animation
|
||||
// When an elevated item is cleared, we reset the elevation using another animation
|
||||
// and set the background to null again so a seam doesn't show up in further actions.
|
||||
|
||||
val view = viewHolder.itemView
|
||||
|
@ -148,6 +146,18 @@ class QueueDragCallback(
|
|||
super.clearView(recyclerView, viewHolder)
|
||||
}
|
||||
|
||||
override fun onMove(
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder,
|
||||
target: RecyclerView.ViewHolder
|
||||
): Boolean {
|
||||
return playbackModel.moveQueueDataItems(
|
||||
viewHolder.bindingAdapterPosition,
|
||||
target.bindingAdapterPosition,
|
||||
queueAdapter
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
playbackModel.removeQueueDataItem(viewHolder.bindingAdapterPosition, queueAdapter)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowInsets
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.core.view.updatePadding
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
|
@ -53,16 +52,11 @@ class QueueFragment : Fragment() {
|
|||
): View {
|
||||
val binding = FragmentQueueBinding.inflate(inflater)
|
||||
|
||||
val callback = QueueDragCallback(playbackModel) { dY ->
|
||||
// By default, CoordinatorLayout is not aware of scroll events originating from
|
||||
// when an item is scrolled off-screen, so we manually add a scroll event ourselves.
|
||||
(binding.queueAppbar.layoutParams as CoordinatorLayout.LayoutParams).behavior
|
||||
?.onNestedPreScroll(
|
||||
binding.queueCoordinator, binding.queueAppbar,
|
||||
binding.queueRecycler, 0, dY,
|
||||
IntArray(2), 0
|
||||
)
|
||||
}
|
||||
val callback = QueueDragCallback(
|
||||
playbackModel,
|
||||
binding.queueCoordinator,
|
||||
binding.queueAppbar
|
||||
)
|
||||
|
||||
val helper = ItemTouchHelper(callback)
|
||||
val queueAdapter = QueueAdapter(helper, playbackModel)
|
||||
|
@ -110,7 +104,6 @@ class QueueFragment : Fragment() {
|
|||
if (isShuffling != lastShuffle) {
|
||||
lastShuffle = isShuffling
|
||||
|
||||
binding.queueRecycler.scrollBy(0, 100)
|
||||
binding.queueRecycler.scrollToPosition(0)
|
||||
}
|
||||
}
|
||||
|
@ -120,8 +113,8 @@ class QueueFragment : Fragment() {
|
|||
|
||||
private fun setupEdgeForQueue(binding: FragmentQueueBinding) {
|
||||
if (isEdgeOn()) {
|
||||
// Account for the side navigation bar if required.
|
||||
binding.root.setOnApplyWindowInsetsListener { v, insets ->
|
||||
// Account for the side navigation bar if required.
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val bars = insets.getInsets(WindowInsets.Type.systemBars())
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ 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 android.view.View
|
||||
|
@ -74,27 +75,43 @@ class AboutFragment : Fragment() {
|
|||
Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
)
|
||||
|
||||
val pkgName = requireContext().packageManager.resolveActivity(
|
||||
browserIntent, PackageManager.MATCH_DEFAULT_ONLY
|
||||
)?.activityInfo?.packageName
|
||||
|
||||
if (pkgName != null) {
|
||||
if (pkgName == "android") {
|
||||
// No default browser [Must open app chooser, may not be supported]
|
||||
openAppChooser(browserIntent)
|
||||
} else {
|
||||
try {
|
||||
browserIntent.setPackage(pkgName)
|
||||
startActivity(browserIntent)
|
||||
} catch (exception: ActivityNotFoundException) {
|
||||
// Not browser but an app chooser due to OEM garbage
|
||||
browserIntent.setPackage(null)
|
||||
openAppChooser(browserIntent)
|
||||
}
|
||||
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.
|
||||
try {
|
||||
requireContext().startActivity(browserIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// No app installed to open the link
|
||||
requireContext().showToast(R.string.err_no_app)
|
||||
}
|
||||
} else {
|
||||
// No app installed to open the link
|
||||
requireContext().showToast(R.string.err_no_app)
|
||||
// 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.
|
||||
val pkgName = requireContext().packageManager.resolveActivity(
|
||||
browserIntent, PackageManager.MATCH_DEFAULT_ONLY
|
||||
)?.activityInfo?.packageName
|
||||
|
||||
if (pkgName != null) {
|
||||
if (pkgName == "android") {
|
||||
// No default browser [Must open app chooser, may not be supported]
|
||||
openAppChooser(browserIntent)
|
||||
} else {
|
||||
try {
|
||||
browserIntent.setPackage(pkgName)
|
||||
startActivity(browserIntent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
// Not browser but an app chooser due to OEM garbage
|
||||
browserIntent.setPackage(null)
|
||||
openAppChooser(browserIntent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No app installed to open the link
|
||||
requireContext().showToast(R.string.err_no_app)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,5 +13,4 @@ Auxio is a local music player with a fast, reliable UI/UX without the many usele
|
|||
- Audio Focus / Headset Management
|
||||
- No internet connectivity whatsoever
|
||||
- Kotlin from the ground-up
|
||||
- Modular, feature-based architecture
|
||||
- No rounded corners
|
||||
|
|
Loading…
Reference in a new issue