Check which url is opened in custom chrome tabs - android

Is there any function in chrome custom tabs analogous to onPageStarted of Webview. IN onNavigation.. the bundle is always null

By design this is not possible with Chrome Custom Tabs. You can tell that a user has navigated but you can't tell where they've gone to. See: http://developer.android.com/reference/android/support/customtabs/CustomTabsCallback.html for details of what's possible.

You can see what URL is currently open in Chrome Custom Tabs if you can get the user to trigger a PendingIntent by clicking on a toolbar action button or a menu option.
In your fragment/activity, create a nested BroadcastReceiver class that will handle the incoming intent in it's onReceive() method:
class DigBroadcastReceiver() : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val uri: Uri? = intent.data
if (uri != null) {
Log.d("Broadcast URL",uri.toString())
main.genericToast(uri.toString())
}
}
}
Add the receiver to your manifest file:
<receiver
android:name=".ui.dig.DigTabs$DigBroadcastReceiver"
android:enabled="true" />
Create the PendingIntent and add it to your CustomTabsIntent.Builder:
val sendLinkIntent = Intent(main,DigBroadcastReceiver()::class.java)
sendLinkIntent.putExtra(Intent.EXTRA_SUBJECT,"This is the link you were exploring")
val pendingIntent = PendingIntent.getBroadcast(main,0,sendLinkIntent,PendingIntent.FLAG_UPDATE_CURRENT)
// Set the action button
AppCompatResources.getDrawable(main, R.drawable.close_icon)?.let {
DrawableCompat.setTint(it, Color.WHITE)
builder.setActionButton(it.toBitmap(),"Add this link to your dig",pendingIntent,false)
}
val customTabsIntent: CustomTabsIntent = builder.build()
customTabsIntent.launchUrl(main, Uri.parse(url))
See my article explaining this on Medium.

Related

How to send Intent.ACTION_SEND inside BroadcastReceiver's onReceive?

I want to add an action to my notification, when the user clicks the button, it shares the text of the notification with other apps, here is my code:
class NotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val message = intent?.getStringExtra("sharedMessage")
val shareIntent: Intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_TEXT, message)
type = "text/plain"
}
shareIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
if (context != null) {
if (shareIntent.resolveActivity(context.packageManager) != null) {
context.startActivity(shareIntent)
}
}
}
}
The onReceive function can receive the click action, but it does not start my shareIntent which should prompt the user to choose an app for sharing. What is the issue?
There are restrictions on background processes (Service and BroadcastReceiver) launching activities. See https://developer.android.com/guide/components/activities/background-starts
This is probably why you aren't seeing the Activity launch.
However, why are you doing this in such a roundabout way? If you want an Activity launched when the user clicks on the notification, just do that directly instead of having the notification click start a BroadcastReceiver.

Nav Deep Link from a foreground service

I'm trying to navigate to a fragment in my navigation graph from a foreground service notification. I used the below code to create an explicit deep link which didn't work out as expected.
val pendingIntent = NavDeepLinkBuilder(this) // this is the Service context!
.setComponentName(MainActivity::class.java)
.setGraph(R.navigation.primary_app_nav)
.setDestination(R.id.shareSheetFragment)
.setArguments(bundle)
.createPendingIntent()
After reading the guide, I got to know that you need to use the activity context and not any other context (in my case it is service context).
Note that if the provided context is not an Activity, the constructor
uses PackageManager.getLaunchIntentForPackage() as the default
activity to launch, if available.
My question is how do I get hold of the MainActivity context from within my foreground service?
I still haven't figured out a way to do it with explicit deep links, but I settled for a solution which leverages implicit deep link.
First, add a deep link to your fragment in the nav graph XML.
<deepLink android:id="#+id/deepLink"
app:uri="my-app://com.example.app/hotspot?ssid={ssid}&passkey={passkey}" />
Add the <nav-graph /> tag within your <activity /> in the manifest. This will ensure that all the intent filters are created and merged to your manifest.
<nav-graph android:value="#navigation/primary_app_nav" />
Create your PendingIntent for the notification as follows.
fun getNotificationPendingIntent(ssid: String, passkey: String): PendingIntent {
val uri = Uri.parse("my-app://com.example.app/hotspot?ssid=$ssid&passkey=$passkey")
val bundle = Bundle().apply { putParcelable(SHARE_DEEP_LINK_KEY, uri) }
val intent = Intent(this, SailsActivity::class.java).apply {
putExtras(bundle)
// to clear the back stack
addFlags(FLAG_ACTIVITY_CLEAR_TOP or FLAG_ACTIVITY_SINGLE_TOP
or FLAG_ACTIVITY_CLEAR_TASK or FLAG_ACTIVITY_NEW_TASK)
}
return PendingIntent.getActivity(this, launchShareSheetCode, intent, PendingIntent.FLAG_UPDATE_CURRENT)
}
Finally, you need to handle the deep link intents from your Activity class' onCreate() method.
private fun handleDeepLinkIntent() {
val deepLink = intent.extras?.getParcelable(SHARE_DEEP_LINK_KEY) as? Uri
deepLink?.also {
val intent = Intent(this, SenderService::class.java)
this.bindService(intent, serviceConn, Context.BIND_ABOVE_CLIENT)
navigateDeepLink(it)
}
}
private fun navigateDeepLink(uri: Uri) {
findNavController(R.id.navHostFragment).apply {
if (graph.hasDeepLink(uri))
navigate(uri)
else {
currentDestination?.getAction(id)?.let {
navigate(id)
}
}
}
}
Deep link URI arguments are automatically available to you in the destination fragment.
if (arguments != null) {
val ssid = requireArguments().getString("ssid")
val passkey = requireArguments().getString("passkey")
}

Intent.Createchooser() Broadcastreceiver is not called

I'm trying to figure out what app users pick when they share content from my app. To achieve this I'm using Inten.createChooser with a custom broadcast receiver. Unfortunately, I can't seem to get the receiver to actually be called.
Running Android 9, I've tried a few different combinations of receiver registration. Making it exported true/false, adding and removing intent-filters (though I can't really find any related to the chooser). The share-chooser itself works just fine and my images are shared. It's just the broadcastreceiver that is not triggering. I can see in logcat that PackageManager has found and registered the receiver.
AndroidManifest.xml (I am aware that exported -should- not be needed)
<receiver android:name=".receivers.ShareBroadcastReceiver"
android:enabled="true"
android:exported="false">
</receiver
The code that creates the share intent (done in a fragment if that matters)
private fun startShareIntent(image: Bitmap){
val receiver = Intent(context, BroadcastReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, PendingIntent.FLAG_UPDATE_CURRENT)
val intent = Intent(Intent.ACTION_SEND)
intent.type = "image/jpg"
// saveTempFile creates a temporary share:able file of the image and returns it's URI.
intent.putExtra(Intent.EXTRA_STREAM, saveTempFile(image))
if (intent.resolveActivity(context!!.packageManager) != null) {
startActivity(Intent.createChooser(intent,
getString(R.string.share_menu_title),
pendingIntent.intentSender))
}
}
class ShareBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("ShareBroadcastReceiver", "Received broadcast")
}
I believe the problem is in creating the Intent object. Instead of
val receiver = Intent(context, BroadcastReceiver::class.java)
it should be,
val receiver = Intent(context, ShareBroadcastReceiver::class.java)

How to handle createChooser's IntentSender without having the component class of the receiver Intent

I am trying to handle the IntentSender of Intent.createChooser() to do something when a user selects an app to share an image on. Most the examples I've found here (posted below), require using a BroadcastReceiver as follows:
Intent receiver = new Intent(context, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, receiver, PendingIntent.FLAG_UPDATE_CURRENT);
String type = "image/*";
Intent share = new Intent(Intent.ACTION_SEND);
share.setType(type);
share.putExtra(Intent.EXTRA_STREAM, awesome_photo_uri);
startActivity(Intent.createChooser(share, "some_title", pendingIntent.getIntentSender()));
My problem with this solution, is located in this line:
Intent receiver = new Intent(context, MyReceiver.class);
The Intent constructor used in these examples require me to make a static MyReceiver class, so I have a class to pass into the second argument of the constructor. But, this causes an issue because I'd like the BroadcastReceiver's onReceive to do stuff in my Fragment. Therefore, I would prefer to create a BroadcastReceiver dynamically in my Fragment.
To no avail, I attempted the following work-around:
Inside MyFragment.kt:
private val receiver: BroadcastReceiver = getBroadcastReceiver()
private val intentFilter = IntentFilter("com.my.app.CHOOSER_ACTION")
override fun onResume() {
requireContext().registerReceiver(receiver, intentFilter)
super.onResume()
}
override fun onPause() {
requireContext().unregisterReceiver(receiver)
super.onPause()
}
private fun shareImage(imageFile: File) {
Intent().apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
action = Intent.ACTION_SEND
type = "image/*"
putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(
requireContext(),
"${myPackageName}.fileprovider",
imageFile
))
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
val receiver = Intent("com.my.app.CHOOSER_ACTION")
val pendingIntent = PendingIntent.getBroadcast(requireContext(), 0, receiver, PendingIntent.FLAG_CANCEL_CURRENT)
startActivity(Intent.createChooser(this, "Share image using", pendingIntent.intentSender))
} else {
startActivity(Intent.createChooser(this, "Share image using"))
}
}
}
private fun getBroadcastReceiver() : BroadcastReceiver {
return object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.extras?.keySet()?.forEach {
Log.v("MyTest", "$it: ${intent.extras?.get(it)}")
}
doSomethingInMyFragment()
}
}
}
Inside AndroidManifest.xml:
<activity android:name="MyActivityThatHasMyFragment" />
<intent-filter>
<action android:name="com.my.app.CHOOSER_ACTION" />
</intent-filter>
</activity>
Unfortunately, the dynamic BroadcastReceiver's onReceive() function is never called after the user presses on a selection. A few questions:
Why does this not work? What am I missing? Am I somehow setting the Intent or IntentFilter incorrectly?
Is it even possible to use a dynamic BroadcastReceiver for handling the IntentSender of createChooser? If not, how can I create a static BroadcastReceiver that triggers something to happen in MyFragment?
Resources:
Get IntentSender object for createChooser method in Android
Get results from Android Chooser
How to tell which app was selected by Intent.createChooser?
What is the purpose of IntentSender?
Intent.createChooser()

How to determine the chosen activity from an intentchooser android

I am trying to determine which intent the user selects from my custom intent chooser, but for whatever reason I can't get onReceive to fire. Here's a sample of my code:
val extraIntents = intentList.toTypedArray()
val receiver = Intent(context, broadcastReceiver.javaClass)
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(context, 0,
receiver, PendingIntent.FLAG_UPDATE_CURRENT)
Intent.createChooser(intentList[0], "choose an intent..",
pendingIntent.intentSender)
startActivityForResult(openInChooser, SELECTOR_CODE)
And earlier, I defined the broadcastReceiver:
broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
I ran this code and set a break code on that onReceive method but it was never triggered. Any help would be greaatly appreciated!
I'm fairly sure you can't set the Intent target to an anonymous BroadcastReceiver, or an inner BroadcastReceiver.
Make it a static sub-class or put it in its own file. You can still construct and register it dynamically. (Sidenote: remember to actually register it. It won't receive Intents if it's not registered.)
Alternatively, use a custom action and don't bother with the explicit target component. Dynamically registered BroadcastReceivers aren't subject to the implicit broadcast limitations in Oreo.

Categories

Resources