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.
Related
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>
Summary
I am attempting to get data sent from the user via the Share menu. In this case, I'll use the basic Android web browser to select text and then share it to my app.
Problem
The first time the user shares the text my app gets the text as expected and displays it via Log.d() -- see the handleSendText() method in the code below.
However, each time thereafter even though the user has selected new text in the web browser and shared it with my app, I still get the original text the user selected (previous value).
Question
How do you reset the Intent -- or whatever it is -- so that I can obtain the new text the user has selected after the first time?
Details
My application has a MainActivity and I've followed the Google docs at :
http://developer.android.com/training/sharing/receive.html
With code like the following in my MainActivity
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
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, "onCreate"); // Handle text being sent
}
}
}
#Override
public void onResume(){
super.onResume();
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, "onResume"); // Handle text being sent
}
}
}
void handleSendText(Intent intent, String callingMethodName) {
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (sharedText != null) {
Log.d("MainActivity", "sharedText : " + sharedText + " called from : " + callingMethodName);
}
}
}
My AndroidManifest section for the activity has the filter added like:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Walk-Thru With Screens and Log
NOTE: Please notice that I've implemented the onResume() in my app also to insure that I don't only get the Intent when onCreate() is called (only one time when the app is started).
Start up browser and grab the text "hurricane".
Choose the app to share with (our test app).
View the log and notice that onCreate() and onResume() are called and value is 'hurricane'
Go back to browser again to share more text...
Select a new word, Atlantic, to share.
Extra note: When we click that Share link this time the Android MenuChooser doesn't display, instead, it automatically opens GrabText again. I found that behavior somewhat odd.
Notice that the Intent text still has the value of hurricane. You can see that there are now two new entries in the logcat.
Attempted Workaround Solutions
I have found that I can destroy the app entirely by overriding onPause() and calling finish() on my Activity (thus closing the entire app) and that seems to work, but isn't there some other way to reset that Intent or the associated text or something?
Do you know of any other way to insure that the new data is retrieved?
I appreciate any help.
UPDATE
Note:I'm updating because there's not a great way to show additional code tried, however, I wouldn't have know to try this without input from other SO User, CommonsWare.
The first answer I received was that I should add an #Override onNewIntent() so I added the following code to my MainActivity:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("MainActivity", "onNewIntent()...");
String action = intent.getAction();
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleSendText(intent, "onNewIntent"); // Handle text being sent
}
}
}
Upon adding that code and running and attempting the copy and then second copy of the new word, I still saw the following in logcat:
It doesn't even look as if the onNewItent() method is even called.
EDIT 2
I altered the emulator Settings...Developer Options... and turned off the "Don't keep activities" setting. It was previously turned on (checked).
After that, I ran the app which contains the onNewIntent() override but now it shows just the one onCreate() gone (which makes sense because the activity is still loaded) but still does not show the onNewIntent() call.
In this sample, I captured the word "remnants".
Edit 3
I built the app and created an APK and deployed it to my Samsung Galaxy Core Prime and I ended up with the same results. onNewIntent() is never called.
I just looked up onNewIntent in Google docs and it states:
onNewIntent(Intent intent) This is called for activities that set
launchMode to "singleTop" in their package, or if a client used the
FLAG_ACTIVITY_SINGLE_TOP flag when calling startActivity(Intent).
I haven't set singleTop so I will try it now. Hmm....
EDIT 4
I have now tried the singleTop variation. I was previously testing on API 15 (v4.0.4)on an emulator so I switched to API 21 (v5.0) to see if there'd be any different.
Here's what the addition of singleTop did to my AndroidManifest.xml:
<activity android:name=".MainActivity" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Notice I also collapsed the two intent-filter nodes into the one.
Selected Text Did Change
On Android API Level 21 the Intent text was now coming in different each time I selected text in the browser.
onNewIntent Is Never Called
However, onNewIntent is NEVER called. I don't ever see it fire.
Share Menu Displayed Every Time
Also, now (on API 21) I see the Share menu every time I select text.
However, I also see an interesting thing when I switch to the browser. You can see multiple copies of the Activity in the list. What?!
Notice also that I implemented the MainActivity as a ListView (scrollable) so I could see the entries even without logcat (for running on real device). That made something else apparent: that the ListView was being updated on each newly shown Activity. But really, it should be the original Activity being appended to.
Creates Numerous GrabText Activities
Yes, now it creates a new GrabText Activity window each time I select text. I thought maybe that was because I had the singleTop set so I removed it but they still appear even after removing singleTop on API LEVEL 21.
Now that I saw it work -- provide different text each time on API 21 I decided to switch back to API Level 15 emulator and try it.
I will report back after I try some things back on API Level 15 again.
API Level 15 : Test Again
I started my other emulator running API Level 15 again and ran the app and even with singleTop set the value is never updated.
You can see this in the logcat and on the updated ListView:
You can also see that the code acts completely different, though I've not changed anything since it appends to the ListView of the one running Activity on api level 15.
I've written a book on this terribly documented thing. I hope this helps someone and that a Google Android dev sees this and explains it.
If your activity already exists, it will be called with onNewIntent() instead of onCreate(). onNewIntent() will be passed the Intent that you need to use for your message.
Try changing the android:launchMode of your activity in the manifest.xml to
singleTop
this way if the activity is already launched, new intents will be received in onNewIntent() method
There is only one answer that actually works, but it could cause other problems.
You just have to decide to call finish() whenever the Activity goes into onPause().
Here's the exact code I implemented which works on all API LEVELS.
#Override
public void onPause(){
super.onPause();
finish();
}
Destroy the Activity
When you add that code then every time you switch back to the app you are sharing from (the web browser in our case) then the onPause will fire on your MainActivity and the finish() method will set the Activity for destruction.
Share Menu Displayed Every Time After This
With this solution every time you select text from your sharing app (web browser) then the Share menu will be displayed and will display GrabText as one of the choices (instead of automatically forcing GrabText to the front again).
Shared Text Is Always the New Text
Since the MainActivity is completely destroyed it then has to be completely loaded (onCreate()) again and so it receives the new Intent text which was sent.
Not A Great Workaround
This isn't a great workaround however, because I believe dialog boxes in your app would also create onPause() to be called and your Activity would be destroyed. Obviously destroying your Activity onPause() just isn't great either because you are beginning to manage "memory" in a way that really should be left to the OS. However, in this case it seems to be the only way around the issue.
I want to make App which is invoked by external App, and allowing it to get some string values in result. I am able to invoke my App, but unable to pass data. So I think something within intent filter is to be modified. Correct me if I am going on wrong track. What value is to be taken for action & category or any other thing from manifest.
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Further description # StartActivity for Result for external App not working
I am able to invoke my App, but unable to pass data
If the caller uses startActivityForResult(), you can use setResult() and finish() at the appropriate point to set the result (in the form of an Intent) and return control to the original activity. For example, if you are presenting a list of items to the user, when the user taps on an item, you can create an Intent with details about the item (e.g., as string extras), pass that Intent to setResult(), and then call finish().
So I think something within intent filter is to be modified.
<intent-filter> elements have nothing technically to do with supplying a result.
I am writing an app that can be launched from another app by receiving an intent with ACTION_VIEW or ACTION_EDIT. For example, it can be opened by viewing an email attachment. The trouble is that when you click on the home button and click again on the launch icon of the email app you were using, my activity is killed and any user edits that had been made are lost. What I want to happen is that when the user clicks the home button, my activity is re-parented so that it resumes when the user clicks on the launch icon of my app. I've tried setting android:allowTaskReparenting="true" in manifest.xml but this doesn't work. Sometimes it doesn't have any effect at all, and sometimes the activity is moved to my launch icon, yet still gets killed when you click again on the email app icon.
The documentation on allowTaskReparenting is really vague. It says the property means:
“Whether or not the activity can move from the task that started it to the task it has an affinity for.”
What does the word can mean here? What I want is a guarantee that the activity does move (and stays there). Is there any way to achieve this?
Thanks in advance to anyone who can help.
EDIT
In response to comments below, I have put together a baby version demonstrating the problems I am encountering. When you start EditFileActivity by clicking on a file in another app (e.g, an attachment to an email) you can then edit the file. But clicking on the home icon and then clicking again on the email app icon causes the changes you have made to the file to be lost. I want the android system to only forget about an instance of EditFileActivity if the user explicitly clicks back and then says "yes" or "no". Ideally I want all instances of EditFileActivity to stack up on my app's launch icon. I could implement something similar to this by using singleTask or singleInstance and writing some kind of activity showing all open files in tabs, but it would be much easier if I could get the android system itself to help me. Any ideas?
Here is a complete project demonstrating the problem.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.Example"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="11"/>
<application
android:label="Example"
android:icon="#drawable/ic_launcher">
<activity
android:name=".LaunchActivity"
android:label="LaunchActivity"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".EditFileActivity"
android:label="EditFileActivity"
android:screenOrientation="portrait">
<!-- This is just an example. I wouldn't use this intent filter in a real app! -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<action android:name="android.intent.action.EDIT"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="file"/>
<data android:scheme="content"/>
<data android:mimeType="*/*"/>
<data android:host="*"/>
</intent-filter>
</activity>
</application>
</manifest>`
LaunchActivity:
public class LaunchActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TextView textView = new TextView(this);
textView.setText("This is the activity you see when you click on the application's launch icon. It does absolutely nothing.");
textView.setTextSize(18);
setContentView(textView);
}
}
EditFileActivity:
public class EditFileActivity extends Activity {
// This String represents the contents of the file.
// In a "real" app the String would be initialised by reading the data from the Intent that started the activity.
// However, for the purposes of this example, the initial value is "Default".
private String fileContents = "Default";
private boolean editsMade = false;
private TextView textView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = new TextView(this);
textView.setText(fileContents);
textView.setTextSize(18);
textView.setPadding(10, 10, 10, 10);
setContentView(textView);
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
makeEdits();
}
});
}
#Override
public void onBackPressed() {
if (editsMade) {
savePrompt();
} else {
finish();
}
}
private void savePrompt() {
DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
if (which == Dialog.BUTTON_POSITIVE) {
// Here is where I would save the edited file.
Toast.makeText(EditFileActivity.this, "File saved", Toast.LENGTH_LONG).show();
}
finish();
}
};
new AlertDialog.Builder(this)
.setTitle("Close File")
.setMessage("Do you want to save the changes you made?")
.setPositiveButton("Yes", listener)
.setNegativeButton("No", listener)
.show();
}
private void makeEdits() {
final EditText editText = new EditText(this);
editText.setText(fileContents);
new AlertDialog.Builder(this)
.setTitle("Edit File")
.setView(editText)
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int whichButton) {
Editable editable = editText.getText();
assert editable != null;
String newContents = editable.toString();
if (!fileContents.equals(newContents)) {
editsMade = true;
fileContents = newContents;
textView.setText(fileContents);
}
}
})
.setNegativeButton("Cancel", null)
.show();
}
}
UPDATE 10/12/2014
The problems encountered were due to the use of the Intent flag FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET.
Fortunately, Google have deprecated this flag, as of API Level 21.
Issue:
The trouble is that when you click on the home button and click again
on the launch icon of the email app you were using, my activity is
killed and any user edits that had been made are lost.
This happens because the email application had set FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET intent flag while launching your activity. When this flag is set, next time when the task is brought to the foreground, your activity will be finished, so that user returns to the previous activity.
From docs:
This is useful for cases where you have a logical break in your
application. For example, an e-mail application may have a command to
view an attachment, which launches an image view activity to display
it. This activity should be part of the e-mail application's task,
since it is a part of the task the user is involved in. However, if
the user leaves that task, and later selects the e-mail app from home,
we may like them to return to the conversation they were viewing, not
the picture attachment, since that is confusing. By setting this flag
when launching the image viewer, that viewer and any activities it
starts will be removed the next time the user returns to mail.
Solution:
Use singleTask launchMode for your activity. The email app will not kill your activity, as the activity belongs to different task now.
If the activity instance is already in the task and an attempt is made to launch the activity again, then a new instance is not created. Instead onNewIntent is called. Here you can prompt the user to save the previous edit if any, before presenting new content.
As discussed above, the system's default behavior preserves the state
of an activity when it is stopped. This way, when users navigate back
to a previous activity, its user interface appears the way they left
it. However, you can—and should—proactively retain the state of your
activities using callback methods, in case the activity is destroyed
and must be recreated.
When the system stops one of your activities (such as when a new
activity starts or the task moves to the background), the system might
destroy that activity completely if it needs to recover system memory.
When this happens, information about the activity state is lost. If
this happens, the system still knows that the activity has a place in
the back stack, but when the activity is brought to the top of the
stack the system must recreate it (rather than resume it). In order to
avoid losing the user's work, you should proactively retain it by
implementing the onSaveInstanceState() callback methods in your
activity.
Source
there is many similar questions of how to resume activity from notification.
But my case is slightly different.
I have an activity which should only be launched from other activity, and never from home or applications list (let me call this "standalone" mode).
This activity is designed to upload file to the server and handles only one intent:
<activity android:name="FileUploader"
android:label="..."
>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<data android:mimeType="*/*"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
So it could be launched, for example, from Gallery or Camera by means of "Share file". But not from applications list, since there is no way to pass file which should be uploaded and I don't want to add any UI for its selection: I want to keep application as simple as possible.
Here is small example: launch Gallery from Home screen, then select photo, tap share and select my FileUploader from the list.
Activities stack is: GalleryMain > Gal2 > ... > FileUploader
Now if I tap Home key - I return to Home screen and after tapping of Gallery again, I return directly to my FileUploader - this is exactly what I need, so this is ok for now.
Now keeping in mind than upload operation takes time I add service which is actually implements upload: FileUploaderService. I also want to add notification which shows current upload progress when (and only when) user leaves FileUploader activity by pressing Home key.
Tapping on this notification should return user to the FileUploader activity, resuming it in case if it is already launched, or starting it again if activity was finished by the system. !But! resuming or starting it in context of "parent" activity (Gallery in example above), since creating it in its own context leads to unwanted behavior: FileUploader icon becomes to show in Recent Apps list, allowing user to launch it in "standalone" mode.
So 2 cases:
1. Resume if already launched
2. Recreate if was destroyed by system.
I have found I way (maybe this is dirty hack, but it works) to implement first:
String className = FileUploader.class.getCanonicalName();
Intent notificationIntent = null;
mAM = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
Iterator<ActivityManager.RunningTaskInfo> iter = mAM.getRunningTasks(1000).iterator();
int pid = -1;
while (iter.hasNext()) {
ActivityManager.RunningTaskInfo ti = iter.next();
if (className.equals(ti.topActivity.getClassName())) {
pid = ti.id;
break;
}
}
if (pid != -1) {
Iterator<ActivityManager.RecentTaskInfo> iter2 = mAM.getRecentTasks(100, 0).iterator();
while (iter2.hasNext()) {
RecentTaskInfo ti = iter2.next();
if (pid == ti.id) {
notificationIntent = ti.baseIntent;
}
}
}
With this code I'm getting intent which was used to launch "parent" activity (eg. Gallery) and then set my notification with this intent. So it resumes Gallery just like from Home screen leading user directly to my activity, which is currently on top of the stack.
But what to do with 2: how to relaunch "parent" Activity (moreover, I even don't know how to find out which activity it was) and recreate its stack to state it was before activity was destroyed?
All "parent" activities are third-party, so I can't change their code.
Any suggestions?
Well, the only way I found to implement this is:
android:excludeFromRecents="true" android:launchMode="singleTask"
This is not 100% of what I want (eg. I can't return to "parent" application) after resume, but is better than nothing.