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
Related
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();
}
}
I am sending intent from "activity" to a receiver in "service" (and pass the data). My code has activity and service (that has reciever). Receiver is declared as follows
<receiver android:name="xxx"
android:enabled="true" >
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
<!-- protected intents meant for os and not for us <action android:name="android.intent.action.ACTION_NEW_OUTGOING_CALL" android:priority="0" /> -->
</intent-filter>
</receiver>
Activity is defined as follows
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
I reviewed
Use an intent to send data to my activity
Intent I am invoking is the call intent and passing the destination call number, with the following code
Log.e(TAG,"Calling "+number);
Intent callIntent = new Intent(Intent.ACTION_CALL); //ACTION_NEW_OUTGOING_CALL is deprecated in API 21, hence ACTION_CALL
callIntent.putExtra("PHONE_NUMBER",number);
number = "tel:"+number;
callIntent.setData(Uri.parse(number));
startActivity(callIntent);
Above code successfully makes a telephone call from my app. I also have a receiver to intercept the calls and the reciever intercepts the above call just fine. However 'extras' of above intent is missing in the receiver; I always get "PHONE_NUMBER" as null in the following code
#Override
public void onReceive(Context context, Intent intent) {
//blah blah..
savedNumber = intent.getExtras().getString("PHONE_NUMBER");
if(savedNumber == null)
savedNumber = intent.getStringExtra("PHONE_NUMBER");
Log.e(TAG, " savedNumber = "+savedNumber);
}
What is my mistake and why is that I get the intent in the reciever but the 'extras' is missing (as you may have noticed, I tried to get it both ways from intent)
Try this:
Intent intent = context.getIntent();
savedNumber = intent.getStringExtra("PHONE_NUMBER");
getIntent() is method of Activity class. You can see in the onReceive() method has an intent argument, you get string from this.
String number = null;
number = intent.getStringExtra("PHONE_NUMBER");
But i read on this article: How to pass Extra to BroadcastReceiver, when initiating ACTION_CALL
Only the Android system itself can broadcast the NEW_OUTGOING_CALL Intent.
You can't add your own extras to this Intent. You'll need to come up with another way to do whatever it is you are trying to accomplish.
Start service like this-
Intent callIntent=new Intent(this, Service.class);
callIntent.putExtra("phonenumber",number);
this.startService(callIntent);
Then retrieve data from the service;
data=(String) intent.getExtras().get("phonenumber");
You can access your parameter from either the onHandleIntent or onStartCommand Intent parameter.
Service
protected void onStartCommand (Intent intent, int flags, int startId) {
data=(String) intent.getExtras().get("data");
}
IntentService
protected void onHandleIntent(Intent intent) {
data=(String) intent.getExtras().get("data");
}
It depends on which type of service you are running.
I am trying to pass some values by using a bundle through a pending intent to my activity from a service. If the app is just started everything works fine but when the app is in resume mode, though my service receives new values from the remote server and put them in the pendingintent to pass to the activity, the activity shows the old values. Here is the code on service side:
private void sendNotification(String wholemsg) {
mNotificationManager = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Intent.ACTION_MAIN);
notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);
/* Do something to extract salesID and notificationmessage from wholemsg
....
....
....
salesID=....
notificationmessage=...
....
*/
Bundle bundle=new Bundle();
bundle.putString("msg", notificationmessage);
bundle.putString("strsalesID", salesID);
notificationIntent.replaceExtras(bundle);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_ONE_SHOT+PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("AppV001 Notification")
.setStyle(new NotificationCompat.BigTextStyle().bigText(notificationmessage))
.setContentText(notificationmessage);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
And this is onRestart method on my activity:
#Override
protected void onRestart() {
super.onRestart();
NotificationManager mNotificationManager = (NotificationManager) this
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancel(1);
try {
Intent intent = getIntent();
if (intent.hasExtra("msg") && intent.hasExtra("strsalesID")) {
String strmsgtitle = intent.getStringExtra("msg");
salesID = intent.getStringExtra("strsalesID");
titletext.setText(getString(R.string.str_ettitle) + salesID);
}
} catch (Exception ex) {
return;
}
}
The problem is that the salesID holds the previous value when the app comes back from hidden mode. It seems that the service cannot change the bundle of activity while it is hidden.
Many thanks in advance for your time!
I have found what was wrong with what my code and I want to post it here in case someone else faced the same problem. acj was right and there was a problem with my launch mode. My original application manifest was this:
<activity
android:name="com.example.appv001.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Since the launch mode wasn't declared the standard mode was taken automatically. We can have four launchMode(look at this):
The 'standard' is the default value. The four values fall into two
groups:
'standard' and 'singleTop' can instantiate multiple activity instances and the instance will stay in the same task.
For 'singleTask' or 'singleInstance', the activity class uses the singleton pattern, and that instance will be the root activity of a
new task.
Since my activity had standard launch mode, when the service was calling it a new instance of the activity was created and the intent was passed to the this new activity. Therefore instead of invoking "onNewIntent()", the "onCreate" method was invoked, in the other words the "onNewIntent()" was never called to set the bundle and etc. I changed the activity manifest to this and set the launch mode to singleTask (android:launchMode="singleTask) and the problem just solved:
<activity
android:name="com.example.appv001.MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
I hope this helps someone else.
I'm initiating new call from my activity. And trying to pass a boolean extra.
public class CallInitiatingActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + number));
intent.putExtra("com.demoapp.CUSTOM_CALL", true);
startActivity(intent);
}
}
I also have a BroadcastReceiver registered, which listens for outgoing calls:
<receiver android:name=".OutgoingCallListener" android:exported="true" >
<intent-filter android:priority="0" >
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
Basically what I'm expecting onReceive to see my extra, but somehow it is not passed:
public class OutgoingCallListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
for (String key : extras.keySet()) {
Log.i(Constant.LOG_TAG, key + ": " + extras.get(key));
}
}
}
Output:
android.phone.extra.ALREADY_CALLED:false
android.intent.extra.PHONE_NUMBER:+370652xxxxx
com.htc.calendar.event_uri:null
android.phone.extra.ORIGINAL_URI:tel:+370652xxxxx
Your custom extra is present in the Intent that you use to start the "call" activity. But it isn't copied into the NEW_OUTGOING_CALL Intent that is broadcast as a part of the actual call mechanism. These 2 operations are distinct and only indirectly related to each other. Only the Android system itself can broadcast the NEW_OUTGOING_CALL Intent.
You can't add your own extras to this Intent. You'll need to come up with another way to do whatever it is you are trying to accomplish.
I have 2 issues with reading NFC Tags.
First is Tag Read Activity creates each time when tag received.
And second issue is activity opens in full screen window, not under Tab Host Activity, but first issue is worst.
What do I do ( AndroidManifest.xml ):
<activity
android:name="readingActivity" >
<intent-filter>
<action android:name="android.nfc.action.TAG_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
and readingActivity.cs:
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d("W", "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.readingActivity);
}
#Override
public void onResume() {
super.onResume();
Log.d("W", "onResume");
PendingIntent intent = PendingIntent.getActivity(this, 0, getIntent(), 0);
NfcAdapter.getDefaultAdapter(this).enableForegroundDispatch(this, intent,
null, null);
}
#Override
protected void onPause() {
super.onPause();
if(NfcAdapter.getDefaultAdapter(this) != null)
NfcAdapter.getDefaultAdapter(this).disableForegroundDispatch(this);
}
Logs:
02-28 18:22:19.949: D/W(4513): onCreate
02-28 18:22:19.949: D/W(4513): onResume
02-28 18:22:21.078: D/W(4513): onCreate
02-28 18:22:21.082: D/W(4513): onResume
The problem is in the PendingIntent. The getIntent() retrieves the Intent that started your Activity, so passing it to the PendingIntent will result in starting it another time.
Instead of getIntent() use something like new Intent(this.getApplicationContext(), this.getClass()).