I have an application. I want it to appear when I tap "share" in other applications. Then I want it to process the data it has received and go back to the caller application.
E.g. I am sharing a facebook post:
1) My app opens
2) Saves info to DB
3) Returns to FB post
How can this return to FB post be achieved?
I tried referring to callingActivity, but it is null.
Intent filter:
<intent-filter android:label="#string/app_name">
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="*/*"/>
</intent-filter>
Activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val intent = intent
val action = intent.action
val type = intent.type
if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
// do some processing
// goes recursively
startActivity(intent)
}
}
Whatever app you shared from will be active behind your current application so you will be able to share to your app and then just stop your app using finish(). I did a quick test and was able to share from a couple of apps using this approach. Your app will flash up then the user will land back in the app they shared from after your database write is complete. I logged the intent to make sure it was coming through properly and was able to see links from chrome (as an example).
if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) {
// do some processing
Log.d("Extras", intent.getStringExtra(Intent.EXTRA_TEXT))
// goes recursively
finish()
}
Related
I'm developing an app that receives data using intents(ACTION_SEND). I've discovered a weird issue when I'm sharing content from Chrome(only happens in Chrome 83 or above, older versions can't reproduce the issue) for instance, if the content is only selected text, for example, the data reaches my app correctly, but if the content shared is a URL, it reaches my app, but suddenly my app replaces Chrome in the app switcher. So, if I have my app and Chrome opened, after the data sharing I have two instances of my app(even if the Chrome icon appears in top of that window, if I tap it, opens my app).
Any ideas about what is going on here? It's worth to notice that I can't reproduce this using Firefox.
My activity code:
class MainActivity : HybridActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Log.d("MyApp", "onCreate")
super.onCreate(savedInstanceState)
val testButton = Button(this)
setContentView(testButton)
}
override fun onStart(){
super.onStart()
var bundle = intent.getExtras();
Log.d("MyApp", "onStart intent tostr: " + intent.toString())
if (bundle != null) {
bundle.keySet().forEach {
Log.d("MyApp", "EXTRA:" + it + "=" + bundle.get(it));
}
}
}
override fun onResume() {
super.onResume()
Log.d("MyApp", "onResume")
}
}
Intent filter is defined in the manifests like this:
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
<data android:mimeType="image/*" />
<data android:mimeType="video/*" />
</intent-filter>
PS: Don't know if this is related but I've changed the launch mode in the manifest to singleInstance and the issue still is reproducible...
PS2: I have added this activity dump, produced just as the issue appeared:
https://gist.github.com/Leprosy/f63bf02bb1c2887f0e419799d98635ab
Your app is actually not replacing chrome, but instead the share link intent receiver activity from your app arrives at the top of the chrome task. It's still the chrome task, it's just that the top activty is from your app. This ensures that pressing back results in going back to chrome, even if you would switch apps a few times first.
As a workaround, you could detect this, and in that case, create a new intent to launch a new activity and close the one that directly received it.
Edit: I tested this and it can be done in the following way:
import android.app.Activity
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
class ExternalTextReceiverActivity : Activity() {
val CUSTOM_EXTRA_NAME = "RELAUNCHED"
override fun onCreate(savedInstanceState: Bundle?) {
if (!intent.getBooleanExtra(CUSTOM_EXTRA_NAME, false)) {
this.finish()
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(CUSTOM_EXTRA_NAME, true);
startActivity(intent);
}
super.onCreate(savedInstanceState);
// The rest of your onCreate code here
}
}
If you only want this for chrome specifically, then you can use this code:
import android.app.Activity
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
class ExternalTextReceiverActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
if (intent.getIntExtra("org.chromium.chrome.extra.TASK_ID", -1) == this.taskId) {
this.finish()
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
super.onCreate(savedInstanceState);
// The rest of your onCreate code here
}
}
Combine in both cases with android:launchMode="singleInstance" or android:launchMode="singleTask" for your Activity in the Android Manifest to make sure it does not open multiple tasks for this same activity.
Update 2
So I have done some more research and it seems that this only happens when using the android sharesheet. When the app that shares the data uses the intent resolver the activity is opened in a new task/instance. For example the sharing function in firefox does handle it correctly. Unfortunately I could not find anything related to this and how you could fix it.
Original Answer
I think that this a wanted behavior as your app is handling the url and it is not opened in your app.
Update
This is what the offical docs say about implicit deep links you are using:
An implicit deep link is a URI that refers to a specific destination in an app. When a URI is invoked—for example, when a user clicks a link—Android can then open your app to the corresponding destination.
When triggering an implicit deep link, the state of the back stack depends on whether the implicit Intent was launched with the Intent.FLAG_ACTIVITY_NEW_TASK flag:
If the flag is set, the task back stack is cleared and replaced with the deep link destination. As with explicit deep linking, when nesting graphs, the start destination from each level of nesting—that is, the start destination from each element in the hierarchy—is also added to the stack. This means that when a user presses the Back button from a deep link destination, they navigate back up the navigation stack just as though they entered your app from its entry point.
If the flag is not set, you remain on the task stack of the previous app where the implicit deep link was triggered. In this case, the Back button takes you back to the previous app, while the Up button starts your app's task on the hierarchical parent destination within your navigation graph.
From my understanding that means, that only the app that launches your app can change this behavior.
Source: https://developer.android.com/guide/navigation/navigation-deep-link#implicit
I'm using a TileService as a shortcut to open my app's Activity. It does only that, and has no state.
It looks like this:
class QuickAccessTileService : TileService() {
override fun onClick() {
super.onClick()
val intent = Intent(this, SlideOverActivity::class.java)
.addFlags(FLAG_ACTIVITY_NEW_TASK)
startActivityAndCollapse(intent)
}
}
and it's declared in AndroidManifest as:
<service
android:name=".service.QuickAccessTileService"
android:icon="#drawable/ic_home"
android:label="#string/tile_label"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
As it stands, this works. However, when my application is stopped, the next time I try to open it via the quick settings panel, it takes several seconds to start the activity.
Here's what I know:
The Activity in itself isn't slow to start. Trying to open it via the launcher instead makes it quite clear.
The Service seems to be what's taking a while to start before onClick is even executed. It makes sense; the Service is probably not kept running in the background doing nothing while the app isn't running. However, this means that when the system detects a click on my Tile, the Service has to be recreated first, which takes way too long.
I'm not sure where to go from here — if my guesses are even right.
EDIT: As an important addition, I can reproduce this on a OnePlus 7 Pro running on Android Pie. This might be a OnePlus-specific issue as I cannot reproduce it on an emulator.
#RequiresApi(Build.VERSION_CODES.N)
class TileSettingClass : TileService() {
override fun onClick() {
super.onClick()
val tile = qsTile
if (tile.state == Tile.STATE_INACTIVE) {
tile.state = Tile.STATE_ACTIVE
val intent = Intent(this.applicationContext, YourActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.action = "yourAction"
applicationContext.startActivity(intent)
} else {
tile.state = Tile.STATE_INACTIVE
}
tile.updateTile()
}
}
Manifests
<service
android:name=".main.service.TileSettingClass"
android:icon="#drawable/ic_tile_setting"
android:label="#string/milliy"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE"
>
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
I’m trying to show my native script app as a chooser intent when whats app profile image share button on top right was clicked, and want to know how to handle those things within my app. Where do I need to add the code to display image inside my app upon click my app in the chooser intent?
i'have found similar issue here
How to show chooser Intent with my own android app on click of WhatsApp location?
where it is given for map, but i want it for image.
I expect to open the image of whats app profile, on tap of intent and display that image inside my app. and let the user choose save or cancel in user defined folder structure. can anyone guide me?
i tried this to handle incoming image but its not working.
let intent = android.getIntent();
let action = android.intent.getAction();
let type = android.intent.getType();
if (android.intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleSendText(intent); // Handle text being sent
} else if (type.startsWith("image/")) {
handleSendImage(intent); // Handle single image being sent
}
} else if (android.intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
if (type.startsWith("image/")) {
handleSendMultipleImages(intent); // Handle multiple images being sent
}
} else {
// Handle other intents, such as being started from the home screen
}
function handleSendImage(intent) {
var imageUri = intent.getParcelableExtra(android.intent.EXTRA_STREAM);
console.log("-----image uri-------");
console.log(imageUri);
if (imageUri != null) {
// Update UI to reflect image being shared
}
}
You have to update your intent-filter for NativeScript Activity to let system know your Activity can handle selection of an image.
<activity android:name="com.tns.NativeScriptActivity"...>
...
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
I added this code to my manifest, in order to handle sharing from Instagram to my application:
<activity android:name=".ActivityHandlingFragments">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
And the following code is censed to handle the sharing (this function is called in onCreate(...) { ... }:
public void handleIntent() {
Intent intent = getIntent();
if(intent != null) {
Toast.makeText(getApplicationContext(), intent.getStringExtra("XYZ"), Toast.LENGTH_LONG).show();
}
}
By what could I replace XYZ in the above code? In other words: is there any list of all the data that Instagram passes into its implicit intent?
Note: https://www.instagram.com/developer/mobile-sharing/android-intents/ lists only intents from an application to Instagram (I want to contrary!).
Important edit:
Using getExtras() method, I discovered there is only one extra: android.intent.extra.TEXT, which contains the URL of the Instagram publication that is shared from Instagram to my app.
So, to get it, write: String url = intent.getStringExtra("android.intent.extra.TEXT");
Waiting for confirmations...! :) In particular: is there any legal mean to get the image (or video), the comments and the legend of the publication (the legend is the text the Instagramer write while posting the image or video and it appears just below the media, and above the comments)?
Using getExtras() method, I discovered there is only one extra: android.intent.extra.TEXT, which contains the URL of the Instagram publication that is shared from Instagram to my app.
So, to get it, write: String url = intent.getStringExtra("android.intent.extra.TEXT");
Waiting for confirmations...! :) In particular: is there any legal mean to get the image (or video), the comments and the legend of the publication (the legend is the text the Instagramer write while posting the image or video and it appears just below the media, and above the comments)?
I am using the share intent to let the user share image within my application,so when user shares an image my activity sends the image to server and shows up in app. The problem is that when user go back and resume the app again then the same intent process is happening again.
here's the intent handling code which i am calling in onCreate() method
public void handleIntents(){
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
// handleSendText(intent); // Handle text being sent
} else if (type.startsWith("image/")) {
handleSendImage(intent); // Handle single image being sent
}
} else if (Intent.ACTION_SEND_MULTIPLE.equals(action) && type != null) {
if (type.startsWith("image/")) {
//handleSendMultipleImages(intent); // Handle multiple images being sent
}
} else {
// Handle other intents, such as being started from the home screen
}
}
here's the manifest declaration
<activity
android:name=".HomeScreen"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
<!-- <data android:mimeType="audio/*" />
<data android:mimeType="video/*" />-->
</intent-filter>
</activity>
So,when I consumed the intent then it should not happen unless a new intent is again created.
Note: I would suggest please create the same situation,try the code on your system.
What I want is ; I share an image from gallery to this app.Now I press back then open this activity from recents,
It should not try to resend the image.
When I go back and again select an image and share with my app then
it should.
After you return to the same activity, Android will reuse the same intent to ensure same state can be achieved, try setIntent(null) after handling the image
From your comment i think i get the use case now,
thats happens because the activity wasn't finished so each time onCreate method is called because either the screen rotated or the system recreated the activity (going to background or such), onCreate is called and will pass you the same intent that was used to start the activity in the first time, what you can do in that case is check for the Bundle savedInstanceState that you receive on the onCreate method, if it is null then its the first time the activity is being created and you should upload the image otherwise the activity is being recreated and you don't need to upload the image.
Also because the image may or may not had be uploaded with success, the screen may rotated and the upload stoped, etc (this will depend on how you do your upload, AsyncTaks, Thread, Services etc) you may actually want to try to upload the image again, in that case you can do as #eduyayo suggests and use an extra to indicate that the image has been uploaded already
You can always overwrite what's being called on onResume().
Another idea that comes to mind now, you could use threads.And onResume, you make sure that thread won't start.How?A boolean should do the job.I will write you some pseudo-code so you can understand what I am saying.
Boolean name = false;
onCreate(){
if(name = false){
Run ur thread now
}
}
onResume(){
if(name = false){
Run ur thread now
}
}
That should do the job,mate.Let me know if you need more info.I'll be back later a bit.