Android onNewIntent() not triggered when activity is invisible - android

I have 2 activities, first one being MainActivity. In Manifest launch mode of Second Activity is set as SingleInstance.The button in MainActivity starts SecondActivity, which has a button which triggers notification. The only problem is onNewIntent() method of SecondActivity is not called when SecondActivity is not visible.Why isn't the onNewIntent() method not called when app is in background or SecondActivity is invisble to the user?
Code:
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.globemaster.com.trials">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"
android:launchMode="singleInstance"
android:parentActivityName=".MainActivity"/>
</application>
</manifest>
Java:
MainActivity.java:
Intent intent=new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
SecondActivity.java:
public class SecondActivity extends AppCompatActivity {
int count=0;int id=3;Notification notification;
NotificationManager notificationManager;
Intent intent,intent1;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_second);
findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
count++;
intent1=new Intent(getApplicationContext(),SecondActivity.class);
if (count>1){
intent1.putExtra("click",true);
}
PendingIntent pendingIntent=PendingIntent.getActivity(getApplicationContext(),0,intent1,PendingIntent.FLAG_UPDATE_CURRENT);
if (notificationManager==null)
{
notificationManager= (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
Notification.Builder builder=new Notification.Builder(getApplicationContext());
builder.setSmallIcon(R.drawable.ic_launcher_background);
builder.setContentTitle("My Notification");
builder.setContentText("You have "+count+" message");
builder.setContentIntent(pendingIntent);
notification=builder.build();
notificationManager.notify(3,notification);
} });
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.e("OnNewIntent","Called");
if (intent!=null&&intent.hasExtra("click")&&intent.getBooleanExtra("click",false))
{
Toast.makeText(getApplicationContext(), "Notification Clicked", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(getApplicationContext(), "False", Toast.LENGTH_SHORT).show();
}
}
}

According to the documentation
This is called for activities that set launchMode to "singleTop" in their package, or if a client used the Intent.FLAG_ACTIVITY_SINGLE_TOP flag when calling startActivity(Intent).
Based on that, you could try either:
setting the android:launchMode in the manifest to singleTop; or
adding the flag to the Intent via intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) before calling startActivity
If you:
need the singleInstance behaviour; and
want to be informed when an Activity that is in the background has come to the foreground (for example in the singleInstance case); and
don't need the Intent explicitly
then you can implement the onResume method, which will be called when the user returns to the Activity.
Edit after testing
What I've observed in testing is that, if a singleInstance Activity is in the background and Intents are passed to it (via startActivity for example) these are queued until the Activity comes into the foreground, then:
onNewIntent is called for each Intent; then
onResume is called once
This contradicts the documentation, but could be used to solve your problem.
As noted in the documentation:
Note that getIntent() still returns the original Intent. You can use setIntent(Intent) to update it to this new Intent.
So, if you are happy to process the Intents when the Activity comes to the foreground, you could replace your onNewIntent above with:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
#Override
protected void onResume() {
super.onResume();
Intent intent = getIntent();
if (intent != null && intent.hasExtra("click") && intent.getBooleanExtra("click", false)) {
Toast.makeText(getApplicationContext(), "Notification Clicked", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "False", Toast.LENGTH_SHORT).show();
}
}

Related

error at onHandleIntent cannot get receiver

I need to call onHandleIntent and receive a value from app1 and get result in app2 without user notices ( different apps, one activity will call the servixe of other app).
However, when onHandleIntent is starting in this line:
mReceiver = workIntent.getParcelableExtra("receiver");
Brings the error
E/Parcel: Class not found when unmarshalling: com.example.idscomercial.myapplication.AddressResultReceiver
java.lang.ClassNotFoundException: com.example.idscomercial.myapplication.AddressResultReceiver
...
Caused by: java.lang.ClassNotFoundException: Didn't find class "com.example.idscomercial.myapplication.AddressResultReceiver" on path: DexPathList[[zip file "/data/app/com.veritran.vttokentest-2/base.apk"],nativeLibraryDirectories=[/data/app/com.veritran.vttokentest-2/lib/arm, /vendor/lib, /system/lib]]
I actually read :
https://stackoverflow.com/questions/28589509/android-e-parcel-class-not-found-when-unmarshalling-only-on-samsung-tab3
But still I cannot find the solution
This is the code:
App1
public class servicev extends IntentService {
protected ResultReceiver mReceiver;
public servicev() {
super("yeah");
}
#Override
protected void onHandleIntent(Intent workIntent) {
Toast b = Toast.makeText(this.getApplicationContext(), "onHandle starting", Toast.LENGTH_LONG );
b.show();
Log.d("just", "tellme");
String dataString = workIntent.getDataString();
mReceiver = workIntent.getParcelableExtra("receiver");
// mReceiver = new AddressResultReceiver(new Handler());
deliverResultToReceiver(1,"value of servicev");
}
private void deliverResultToReceiver(int resultCode, String message) {
Bundle bundle = new Bundle();
bundle.putString("receiver", message);
mReceiver.send(resultCode, bundle);
}
}
App2:
public class MainActivity extends AppCompatActivity {
protected ResultReceiver mResultReceiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startIntentService();
setContentView(R.layout.activity_main);
}
protected void startIntentService() {
//Intent intent = new Intent();
mResultReceiver = new AddressResultReceiver(new Handler());
Intent intent=new Intent("com.veritran.vttokentest.servicev.START_SERVICE");
intent.setPackage("com.veritran.vttokentest");
//Intent intent = new Intent(this, FetchAddressIntentService.class);
intent.putExtra("receiver", mResultReceiver);
startService(intent);
}
}
==> 2
public
class AddressResultReceiver extends ResultReceiver {
public AddressResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
int duration = Toast.LENGTH_LONG;
String mAddressOutput = resultData.getString("1");
//Toast toast = Toast.makeText(context, "hi brow", duration);
//toast.show();
if (resultCode == 1) {
}
}
}
Manifest app1:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.veritran.vttokentest">
<application
android:allowBackup="false"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name="com.veritran.vttokentest.MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:enabled="true"
android:exported="true"
android:name="com.veritran.vttokentest.servicev">
<intent-filter>
<action android:name="com.veritran.vttokentest.servicev.START_SERVICE" />
</intent-filter>
</service>
</application>
</manifest>
There could be a couple things going on here. First you are going to need a receiver to ever get information back from your service to the main activity. To create a receiver, make a new class that looks like this:
public class myReceiver extends ResultReceiver {
private Receiver mReceiver;
public myReceiver(Handler handler) {
super(handler);
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
public interface Receiver {
void onReceiveResult(int resultCode, Bundle resultData);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
}
}
Then, in your main activity, create the receiver onCreate() with the following code:
mReceiver = new myReceiver(new Handler());
mReceiver.setReceiver(this);
This will be necessary to actually get any data back. you will also need to pass the receiver into the intent as an extra intent.putExtra("receiver", mReceiver); in the section where you define the intent. Then, in the IntentService, you can send your info back to the MainActivity with workIntent.getParcelableExtra("receiver").send(1, b) (where 1 is the result code you want and b is a Bundle of all the info you want to send back).
As far as why your service is never being called, I would check here to see if you are actually sending a correct intent to the service. You should also try removing the intent-filter from your service as it is unnecessary.
Generally you are not going to want to create a new MainActivity inside the service and use the receiver instead. Otherwise your service appears to be correct so I would make sure you check the intent that you are trying to pass in. If that still fails there are a number of other threads here about this problem.
Android IntentService not starting
Reasons why an IntentService won't start? [closed]
I am new here so please let me know if you need more help.

Intent from notification isn't passed to target activity in Android

In my app I have starting activity which starts/restore app's activities and pass to it all intent extras if there are any:
manifest:
<activity android:name=".StartActivity"
android:theme="#style/Theme.AppCompat.Light.NoActionBar.FullScreen"
android:screenOrientation="sensorLandscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Code:
public class StartActivity extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preloader);
String id=getIntent().getStringExtra("id");
Log.d("push~","start activity oncreate:"+id);
Intent i = new Intent(getIntent());
openLastActivity(i);
finish();
}
#Override
protected void onNewIntent(Intent intent) {
String id=intent.getStringExtra("id");
Log.d("push~","start activity new intent:"+id);
openLastActivity(intent);
super.onNewIntent(intent);
}
private void openLastActivity(Intent i){
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP);
if(GameActivity._this!=null) {
i.setClass(this,GameActivity.class);
startActivity(i);
} else {
i.setClass(this,SelectorActivity.class);
startActivity(i);
}
}
}
Also my app has service which generates notifications with following content intent:
Intent notificationIntent= new Intent(context, StartActivity.class);
notificationIntent.putExtra("id",id);
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(contentIntent);
I expect id string will be passed to the last active activity.
The problem is when I run the app via notification tap it starts app and pass id as expected but if there is another notification(when I'm still in the app) neither onCreate nor onNewIntent method of the StartActivity is called therefore no data are passed to main activities.
UPD:Don't know why but android:launchMode="singleTask" for StartActivity solved my problem.Now all intents are receiving in onCreate
Don't know why but android:launchMode="singleTask" for StartActivity solved my problem.Now all intents are receiving in onCreate

Multiple Instances of Activity

We have already the check of CATEGORY_MAIN and !isTaskRoot() but even then 2 instances of activity are launched.
SplashActivity.java
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("OnCreate method.");
if(checkIfActivityIsBroughtToFront() || checkIfActivityIsRootTask()) {
return; // Found that if we finish and don't return then it will run the code below, hence start the recovery task.
}
Log.i("Checking if Recovery is required ...");
new RecoveryTask(SplashActivity.this, this).execute();
}
private boolean checkIfActivityIsBroughtToFront() {
if ((getIntent().getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
// Activity was brought to front and not created,
// Thus finishing this will get us to the last viewed activity
Log.i("Detecting a brought to front, no need for recovery.");
finish();
return true;
}
return false;
}
private boolean checkIfActivityIsRootTask() {
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
finish();
return true;
}
}
return false;
}
Logs
2015-10-22 13:42:25.581 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.587 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.637 +0300 SplashActivity INFO[main] - OnCreate method.
2015-10-22 13:42:25.638 +0300 SplashActivity INFO[main] - Checking if Recovery is required ...
2015-10-22 13:42:25.828 +0300 GeoFenceManager INFO[pool-5-thread-1] - Removing geofences ...
2015-10-22 13:42:25.872 +0300 GeoFenceManager INFO[pool-5-thread-2] - Removing geofences ...
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="x.y.z"
android:installLocation="internalOnly" >
<application
android:name=".global.GlobalInstance"
android:allowBackup="true"
android:allowClearUserData="true"
android:hardwareAccelerated="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:largeHeap="true"
android:persistent="true" >
<activity
android:name=".activity.SplashActivity"
android:label="#string/app_name"
android:screenOrientation="portrait"
android:theme="#style/Theme.Background" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".receiver.BootUpReceiver"
android:enabled="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
UPDATE:
This is happening after restart, the BOOT_COMPLETED listener is following
public class BootUpReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, SplashActivity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
Any idea how this can be prevented?
Add android:launchMode="SingleTask" to the splash activity element in the xml. And then on leaving the activity i.e. navigating away from the splash call finish(). The reason you should use this pattern as opposed to "SingleInstance" is that the user can never navigate back to the splash with the back-key (as this is not normal behavior).
private boolean checkIfActivityIsRootTask() {
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
Log.i("Main Activity is not the root. " + "Finishing Main Activity instead of launching.");
finish();
return true;
}
}
return false;
} ?
if this activity is roottask,it return false;
what's RecoveryTask for?
You said:
We have already the check of CATEGORY_MAIN and !isTaskRoot()
But both the check failed. In your logs that you attached, when both instances are created, neither Detecting a brought to front, no need for recovery. nor Main Activity is not the root. " + "Finishing Main Activity instead of launching. is printed in the logs. This means that in both the methods the if part is never encountered and hence finish() never gets executed. Both the methods returned false.
Also, just because you call finish() and return in onCreate(), it doesn't mean that onStart() or onResume() won't be called.
Why does this happen? Launcher activity will never be called twice (considering that you don't internally start the activity). Probably a bug in the some SDK that the user has.
POSSIBLE FIX:
You could try setting android:launchMode="singleTop" in your manifest for the SplashActivity. This will make sure only one instance is maintained.

Starting Android application at boot completion: is my solution overly complicated?

I have an application that I would like to have automatically start following boot completion. The following code seems overly complicated and I get erratic application starts when swiping to a neighbouring workspace.
What am I missing here? I have an activity class, a service class, as well as a broadcast receiver. Below is my code (in that order) followed by the manifest.
public class BlueDoor extends Activity implements OnClickListener{
Button btnExit;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.main);
btnExit = (Button) this.findViewById(R.id.ExitButton);
btnExit.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ExitButton:
System.exit(0);
break;
}
}
}
service.class
public class BlueDoorStartService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
callIntent.setClass(this, BlueDoor.class);
startActivity(callIntent);
// do something when the service is created
}
}
broadcast receiver
public class StartBlueDoorAtBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Intent serviceIntent = new Intent(context, BlueDoorStartService.class);
context.startService(serviceIntent);
}
}
}
Manifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.bluedoor"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<receiver
android:name=".StartBlueDoorAtBootReceiver"
android:enabled="true"
android:exported="false" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:name=".BlueDoorStartService" >
</service>
<activity
android:name=".BlueDoor"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
UPDATE Solution(s), 10/22/2015:
Changing the service to:
public class BlueDoorStartService extends Service {
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
and the receiver to:
public class StartBlueDoorAtBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Start Service On Boot Start Up
Intent serviceIntent = new Intent(context, BlueDoorStartService.class);
context.startService(serviceIntent);
//Start App On Boot Start Up
Intent App = new Intent(context, BlueDoor.class);
App.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(App);
}
}
resulted in a working configuration using a service w/no misbehaving. However deleting the service all together and modifying the receiver thus:
public class StartBlueDoorAtBootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent App = new Intent(context, BlueDoor.class);
App.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(App);
}
}
also resulted in a functional as well as a more concise configuration that starts the application following boot completion.
Your BroadcastReceiver calls
context.startService(serviceIntent)
so the service will be created if it doesn't exist yet (which will be the case shortly after booting) and thus start the activity from its onCreate() method. So the app works, to a certain extent.
BUT when you call startService(), the system always calls the service's onStartCommand() method. You did not override that method, so the system uses the standard implementation from class android.app.Service.
As you can read on grepcode.com, the method will return a value like START_STICKY by default. This tells the system to keep the service alive until it is explicitly stopped.
In your case, I suppose the system reacted to the swiping by temporarily killing and then reanimating (= creating) the service, which in turn started your activity.
Some information on the service lifecycle can be found here.
What you can do:
Override onStartCommand() to start the activity from there instead of from onCreate(). Then use stopSelf(int) like described here
One last thing: when exiting from the activity, don't use System.exit(0) but call finish() instead, see this SO answer for "why".

Unregister Receiver is it necessary?

I have an alarm manager whose receiver I had registered in my code. The whole point of having an alarm manager in case of Timer is that it should run in background when in Pause state. Now, do I unregister it in onPause() or in OnDestroy() and will it still run in background and wake up and the receiver would receive it?
EDIT:
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MyClass.class);
i.putExtra("fromReciever", true);
startActivity(i);
}
I would suggest that you register it in your AndroidManifest.xml file. Example:
<receiver android:name="com.example.android.MyReceiver" >
<intent-filter>
<action android:name="com.example.android.USER_ACTION" />
</intent-filter>
</receiver>
This will keep your receiver registered as long as the application is installed on your device. All you have to do is implement it:
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Toast.makeText(context, "Intent Detected.", Toast.LENGTH_LONG).show();
}
}
And you are set.
Additionally, you can check out this tutorial for more information.
Added
If you want to resume your Activity you can add this code in your Receiver onReceive method.
Intent intent = new Intent(this, YourActivity.class);
intent.putExtra("fromOnReceive", true);
context.startActivity(intent);
Then, in your Activity onCreate method, you check if it was called from your Receiver
#Override
protected void onCreate(Bundle savedInstanceState) {
if(getIntent().hasExtras()){
boolean fromReceiver = getIntent().getExtras().getBoolean("fromOnReceive");
if(fromReceiver)
//Do work
}
}

Categories

Resources