In-built browser intent chooser (#3511)

Co-authored-by: Isira Seneviratne <31027858+Isira-Seneviratne@users.noreply.github.com>
This commit is contained in:
Bnyro 2023-04-10 12:44:41 +02:00 committed by GitHub
parent ad20a1821f
commit e7995b4bd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 160 additions and 19 deletions

View File

@ -338,11 +338,6 @@
android:enabled="true"
android:exported="false" />
<service
android:name=".services.UpdateService"
android:enabled="true"
android:exported="false" />
<service
android:name=".services.BackgroundMode"
android:enabled="true"
@ -354,4 +349,10 @@
android:exported="false" />
</application>
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="*" />
</intent>
</queries>
</manifest>

View File

@ -2,18 +2,41 @@ package com.github.libretube.helpers
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.net.toUri
import androidx.fragment.app.FragmentManager
import com.github.libretube.R
import com.github.libretube.extensions.toastFromMainThread
import com.github.libretube.ui.sheets.IntentChooserSheet
object IntentHelper {
fun openLinkFromHref(context: Context, link: String) {
val uri = Uri.parse(link)
val launchIntent = Intent(Intent.ACTION_VIEW).setData(uri)
fun openLinkFromHref(context: Context, fragmentManager: FragmentManager, link: String) {
val intent = Intent(Intent.ACTION_VIEW)
.setData(link.toUri())
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
@Suppress("DEPRECATION")
val resolveInfoList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.packageManager
.queryIntentActivities(
intent,
PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_ALL.toLong())
)
} else {
context.packageManager
.queryIntentActivities(intent, PackageManager.MATCH_ALL)
}
if (resolveInfoList.isEmpty()) {
try {
context.startActivity(launchIntent)
context.startActivity(intent)
} catch (e: Exception) {
context.toastFromMainThread(R.string.unknown_error)
context.toastFromMainThread(R.string.error)
}
} else {
IntentChooserSheet(resolveInfoList, link)
.show(fragmentManager)
}
}
}

View File

@ -59,7 +59,7 @@ class AboutActivity : BaseActivity() {
private fun setupCard(card: MaterialCardView, link: String) {
card.setOnClickListener {
IntentHelper.openLinkFromHref(this, link)
IntentHelper.openLinkFromHref(this, supportFragmentManager, link)
}
card.setOnLongClickListener {
onLongClick(link)
@ -77,7 +77,7 @@ class AboutActivity : BaseActivity() {
Snackbar.LENGTH_LONG
)
.setAction(R.string.open_copied) {
IntentHelper.openLinkFromHref(this, href)
IntentHelper.openLinkFromHref(this, supportFragmentManager, href)
}
.setAnimationMode(Snackbar.ANIMATION_MODE_FADE)
.show()

View File

@ -35,7 +35,7 @@ class HelpActivity : BaseActivity() {
private fun setupCard(card: MaterialCardView, link: String) {
card.setOnClickListener {
IntentHelper.openLinkFromHref(this, link)
IntentHelper.openLinkFromHref(this, supportFragmentManager, link)
}
}
}

View File

@ -0,0 +1,44 @@
package com.github.libretube.ui.adapters
import android.content.Intent
import android.content.pm.ResolveInfo
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.core.net.toUri
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.IntentChooserItemBinding
import com.github.libretube.ui.viewholders.IntentChooserViewHolder
/**
* An adapter for opening an intent chooser inside the app, example-wise for urls
* @param packages A list of resolved packages found by a package query
*/
class IntentChooserAdapter(
private val packages: List<ResolveInfo>,
private val queryUrl: String
) : RecyclerView.Adapter<IntentChooserViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): IntentChooserViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
val binding = IntentChooserItemBinding.inflate(layoutInflater, parent, false)
return IntentChooserViewHolder(binding)
}
override fun getItemCount() = packages.size
override fun onBindViewHolder(holder: IntentChooserViewHolder, position: Int) {
val currentPackage = packages[position]
holder.binding.apply {
val drawable = currentPackage.loadIcon(root.context.packageManager)
appIconIV.setImageDrawable(drawable)
val appLabel = currentPackage.loadLabel(root.context.packageManager)
appNameTV.text = appLabel
root.setOnClickListener {
runCatching {
val intent = Intent(Intent.ACTION_VIEW, queryUrl.toUri())
.setPackage(currentPackage.activityInfo.packageName)
root.context.startActivity(intent)
}
}
}
}
}

View File

@ -0,0 +1,37 @@
package com.github.libretube.ui.sheets
import android.content.pm.ResolveInfo
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.GridLayoutManager
import com.github.libretube.databinding.BottomSheetBinding
import com.github.libretube.ui.adapters.IntentChooserAdapter
class IntentChooserSheet(
private val packages: List<ResolveInfo>,
private val url: String
) : BaseBottomSheet() {
private var _binding: BottomSheetBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = BottomSheetBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.optionsRecycler.layoutManager = GridLayoutManager(context, 3)
binding.optionsRecycler.adapter = IntentChooserAdapter(packages, url)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@ -18,9 +18,7 @@ import com.github.libretube.ui.dialogs.DownloadDialog
import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.ui.fragments.SubscriptionsFragment
import com.github.libretube.util.PlayingQueue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**

View File

@ -0,0 +1,8 @@
package com.github.libretube.ui.viewholders
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.IntentChooserItemBinding
class IntentChooserViewHolder(
val binding: IntentChooserItemBinding
) : RecyclerView.ViewHolder(binding.root)

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:layout_marginHorizontal="10dp"
android:background="@drawable/rounded_ripple"
android:orientation="vertical"
android:paddingVertical="10dp">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/appIconIV"
android:layout_width="75dp"
android:layout_height="75dp"
android:layout_gravity="center"
android:padding="10dp"
app:shapeAppearance="@style/CircleImageView"
tools:src="@mipmap/ic_launcher_round" />
<TextView
android:id="@+id/appNameTV"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:ellipsize="end"
android:maxLines="1"
android:textSize="11sp" />
</LinearLayout>