How to Update activity when open notification - java

I am working on notification and I have a problem. I have an activity already open when I click on notification I don't want to open it again just update the current activity.
if (!NotificationUtils.isAppIsInBackground(getApplicationContext())) {
Intent pushNotification = null;
if (NotificationType != 0 && NotificationType != 2 && NotificationType != 5 && NotificationType != 26) {
pushNotification = new Intent(getApplication(), SplashScreen.class);
pushNotification.putExtra("NotificationType", NotificationType);
pushNotification.putExtra("ReferenceID", ReferenceID);
pushNotification.putExtra("NotificationID", ReferenceID);
pushNotification.putExtra("isread", ReferenceID);
showNotificationMessage(getApplicationContext(), title, message, time, pushNotification);
} else if (NotificationType == 0 || NotificationType == 2 || NotificationType == 5 || NotificationType == 26) {
showNotificationMessageWithNoAction(getApplicationContext(), title, message, title, null);
}
}
can Anyone tell me how I update the activity when I click on notification?

You just need to declare the launchMode to the singleTask to make ensure that multiple same screens not will open.
There are four launch modes for activity. They are:
1. Standard
2. SingleTop
3. SingleTask
4. SingleInstance
Please refer this link Click here
<activity android:name="YOUR_SPLASH_ACTIVITY"
android:launchMode="singleTask"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
And in the Java code , you just override the onNewIntent method , to refresh activity,
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
/**
* HERE YOU JUST REFRESH , YOUR ACTIVITY
*/
}

I don't know if this is what you need but you can do this
finish();
startActivity(getIntent());
Let me know what you really need.
EDIT:
If you need to keep your activity state intact, you can do this
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("MyString", "Welcome back to Android");
}
This will save your activity state
And then you retrieve UI state like this
#Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
String myString = savedInstanceState.getString("MyString");
}

Best way is to make use of the onNewIntent(Intent) method of your Activity.
You can use the Intent parameter of this method to get your Intent Extras, because getIntent() will give the Intent that started the Activity in the first place.
public class MainActivity extends Activity {
public void onCreate(Bundle SavedInstanceState) {
//Initial loading
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if(intent.getStringExtra("methodName").equals("myMethod")) {
//Update the existing screen
}
}
}

Related

how to set up an nfc reader app that won’t open on its own if the app is closed

I’m doing this application that should read some nfc tags.
It is a kind of treasure hunt and who scans all the tags wins.
I structured the application so that there is an initial login and a set of data is saved in a database so that you can keep track of the players who scanned the tags.
I followed a tutorial to create an activity that can read tags. (Only one line of text should be read from the tags)
This is the code I put in the manifest
<uses-permission android:name="android.permission.NFC"/>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain"/>
</intent-filter>
This is the activity code
public class scanActivity extends AppCompatActivity {
private TextView scanView;
private PendingIntent pendingIntent;
private IntentFilter[] readfilters;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scan);
scanView=findViewById(R.id.scanView);
try {
Intent intent= new Intent(this,getClass());
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
pendingIntent=PendingIntent.getActivity(this,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
IntentFilter intentFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
IntentFilter textFilter = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED,"text/plain");
readfilters = new IntentFilter[] {intentFilter, textFilter};
} catch (IntentFilter.MalformedMimeTypeException e) {
e.printStackTrace();
}
readTag(getIntent());
}
private void enableRead(){
NfcAdapter.getDefaultAdapter(this).enableForegroundDispatch(this,pendingIntent,readfilters,null);
}
private void disableRead(){
NfcAdapter.getDefaultAdapter(this).disableForegroundDispatch(this);
}
#Override
protected void onResume() {
super.onResume();
enableRead();
}
#Override
protected void onPause() {
super.onPause();
disableRead();
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
readTag(getIntent());
}
private void readTag(Intent intent) {
Parcelable[] messages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
scanView.setText("");
if(messages != null){
for (Parcelable message:messages){
NdefMessage ndefMessage = (NdefMessage) message;
for (NdefRecord record : ndefMessage.getRecords()){
switch (record.getTnf()){
case NdefRecord.TNF_WELL_KNOWN:
scanView.append("WELL KNOWN ");
if (Arrays.equals(record.getType(),NdefRecord.RTD_TEXT)){
scanView.append("TEXT: ");
scanView.append(new String(record.getPayload()));
scanView.append("\n");
}
}
}
}
}
}
}
Currently when I approach a tag, the phone tries to open a task, but it ends up reopening the application and it does not even open the task with the textview on which the content of the tag should be written.
I would need to make sure that the application does not reopen again when I approach a tag, but simply switch to the next activity, keeping the previous ones.
Also, I would like to prevent the app from opening itself when approaching a tag.
If the above requests are impossible. I need at least to be able to create an activity that reads the tags without opening other activities.
I thank in advance for those who tried to help me <3.
To prevent the App from being automatically opened automatically when approaching the Tag then remove the intent-filter entries in your manifest
If you don't want the Activity paused and resumed when a Tag is detected then don't use the older NFC API of enableForegroundDispatch use the newer and better API of enabledReaderMode as this will deliver the Tag data to a new Thread in your current activity without pausing and resuming your current activity.
Java example of enableReaderMode

Two apps communicating using Intent, passing data

I'm trying to solve an issue passing data between different apps using Intent.
The scenario is like this:
Main (App 1): User clicks Register
Main App launches Register activity (form) in Register (App 2)
User enters first name, last name etc, clicks Send Back
Register app returning values to Main app
Main app displays user's data
Note that Register activity is not the main activity in Register activity.
I would like to solve this without an additional class and without Broadcasting.
My code for Main App, user clicks Register method:
/* User clicks Register */
public void clickRegister(View view) {
Intent intent = new Intent(Intent.ACTION_SEND);
// Verify it resolves
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
// Start an activity if it's safe
if (isIntentSafe) {
startActivity(intent);
}
}
Manifest file for Register activity in Register App:
<activity android:name="com.example.register.Register">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
Now, this is the code in Register activity, of click Send Back method:
/* User clicks Send Back in register activity */
public void clickSendBack(View view) {
// Create an Intent for Register class
// Intent myIntent = new Intent(this, MainActivity.class);
Intent myIntent = new Intent(Intent.ACTION_SEND);
final EditText firstNameInput = (EditText) findViewById(R.id.firstNameEditText);
final EditText secondNameInput = (EditText) findViewById(R.id.secondNameEditText);
String firstName = firstNameInput.getText().toString();
String secondName = secondNameInput.getText().toString();
myIntent.setAction(Intent.ACTION_SEND);
myIntent.putExtra("firstName",firstName);
myIntent.putExtra("secondName",secondName);
myIntent.setType("text/plain");
// Starts activity
startActivity(myIntent);
finish();
}
And here I'm stuck.
Would love to hear any clarification for this topic, and a example for a solution would also be great.
Thanks!
In the first app, add an intent-filter to receive data back from the second app, which is your Register application.
Now, do the same with your Register application, we need to do this so that we can invoke it from our first app.
The intent-filter is there to make sure that we can send data back. According to https://developer.android.com/guide/components/intents-filters:
To advertise which implicit intents your app can receive, declare one or more intent filters for each of your app components with an element in your manifest file.
From the first app, create an Intent that will bring you to the second app. If you don't want to open up the Android share sheet then I suggest you use a PackageManager that gets all the activities that can receive your data and then find in the list your second app and open it with setComponent() with your intent. (Check my code below)
On to our second app, do the same as you did in the first app but now you can add your extras or data like first name and second name.
Back to our first app, write code that will receive the incoming intent from our second app and there, done!
Refer to:
https://developer.android.com/training/sharing
for more information about sending/receiving data with intents.
Here's a sample code:
First Application's Main Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Receive your data here from your Register app
Intent receivedIntent = getIntent();
String action = receivedIntent.getAction();
String type = receivedIntent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
if ("text/plain".equals(type)) {
handleReceivedData(receivedIntent);
}
}
}
private void handleReceivedData(Intent intent) {
String firstName = intent.getStringExtra("first_name");
String secondName = intent.getStringExtra("second_name");
if (firstName == null || secondName == null) {
Toast.makeText(this, "Cannot received data!", Toast.LENGTH_SHORT).show();
return;
}
// Do here what you want with firstName and secondName
// ...
Toast.makeText(this, "First name: " + firstName +
" Second name: " + secondName, Toast.LENGTH_SHORT).show();
}
public void open(View view) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT,
"Here you can put a message for your 'register' application");
sendIntent.setType("text/plain");
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(sendIntent,
PackageManager.MATCH_DEFAULT_ONLY);
////////////////// Get the other application package name //////////////////
// This is so the user cannot choose other apps to send your data
// In order words, this will send the data back to the other
// application without opening the Android Share sheet
ActivityInfo activityInfo = null;
for (ResolveInfo activity: activities) {
// Specify here the package name of your register application
if (activity.activityInfo.packageName.equals("com.example.registerapp")) {
activityInfo = activity.activityInfo;
break;
}
}
// If the other application is not found then activityInfo will be null
// So make sure you add the correct intent-filter there!
if (activityInfo != null) {
// This will open up your register application
ComponentName name = new ComponentName(activityInfo.applicationInfo.packageName,
activityInfo.name);
sendIntent.setComponent(name);
startActivity(sendIntent);
}
else {
Toast.makeText(this,
"Receiver app doesn't exist or not installed on this device!",
Toast.LENGTH_SHORT).show();
}
}
First Application's Manifest
<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>
Second Application's Receiver Activity (In this case, your Register app)
Note: as you want, this is NOT the main activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_receiver);
// This is NOT the main activity!
}
public void send(View view) {
// I just want to note that I am calling the receiving application: "other application"
EditText firstNameEditText = findViewById(R.id.firstNameEditText);
EditText secondNameEditText = findViewById(R.id.secondNameEditText);
String firstName = firstNameEditText.getText().toString().trim();
String secondName = secondNameEditText.getText().toString().trim();
// Check if any of the inputs are empty
if (firstName.isEmpty() || secondName.isEmpty()) {
Toast.makeText(this, "Text boxes cannot be empty!", Toast.LENGTH_SHORT).show();
return;
}
// Send data back to the other application
Intent sendBackIntent = new Intent();
sendBackIntent.setAction(Intent.ACTION_SEND);
sendBackIntent.putExtra("first_name", firstName);
sendBackIntent.putExtra("second_name", secondName);
sendBackIntent.setType("text/plain");
// Get all the available applications that can receive your data
// (in this case, first name and second name)
PackageManager packageManager = getPackageManager();
List<ResolveInfo> activities = packageManager.queryIntentActivities(sendBackIntent,
PackageManager.MATCH_DEFAULT_ONLY);
////////////////// Get the other application package name //////////////////
ActivityInfo activityInfo = null;
for (ResolveInfo activity: activities) {
// Specify here the package name of the other application
if (activity.activityInfo.packageName.equals("com.example.mainapp")) {
activityInfo = activity.activityInfo;
break;
}
}
if (activityInfo != null) {
// Same as before, this will open up the other application
ComponentName name = new ComponentName(activityInfo.applicationInfo.packageName,
activityInfo.name);
sendBackIntent.setComponent(name);
startActivity(sendBackIntent);
}
else {
Toast.makeText(this,
"Receiver app doesn't exist or not installed on this device!",
Toast.LENGTH_SHORT).show();
}
}
Second Application's Manifest
<activity android:name=".ReceiverActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Happy coding!
Edit: I added explanation to my answer.
Your RegisterActivity should look like so
RegisterActivity is parsing the user data to the MainActivity
/* User clicks Send Back in register activity */
public void clickSendBack(View view) {
// Create an Intent for Register class
final EditText firstNameInput =
findViewById(R.id.firstNameEditText);
final EditText secondNameInput =
findViewById(R.id.secondNameEditText);
String firstName = firstNameInput.getText().toString();
String secondName = secondNameInput.getText().toString();
Intent i = new Intent(RegisterActiivty.this, MainActivity.class);
i.putExtra("firstName", firstName);
i.putExtra("secondName", secondName);
startActivity(i);
}
#################################################################
Your MainActivity should look like so.
The MainActivity is responsible for receiving the data
This line of codes should be called in your onCreate method of MainActivity
String firstName = getIntent().getStringExtra("firstName");
String secondName = getIntent().getStringExtra("firstName")
// if you have a TextView on MainActivity you could display the data you have
//gotten from the RegisterActivity like so
Textview firstName = findViewById(R.id.firstName);
firstName.setText(firstName);
Textview secondName = findViewById(R.id.secondName);
secondName.setText(secondName);
/* User clicks Register */
public void clickRegister(View view) {
startActivity(Intent(this, RegisterActivity.class))
}
##########################################################
Your manifest should look like this
<activity android:name="com.example.register.Register"/>
I am trying to wrap my head around what you meant by Main App and Register.
It sound as though they are two different apps or probably two different
project modules. But if what you are trying to say is MainActivity and
RegisterActivity then the above solution should be able to fix your problem.
What you did wrong:
That intent you were trying to parse in the manifest was not needed. If I understood properly what you are trying to achieve. The same goes for the MainActivity and RegisterActivity. You were using Implicit Intent instead of explicit Intent. And when calling findViewById this extra (EditText) was not needed because it is redundant. For more on the intent you can check this
However, this guide should be useful to anyone seeking to parse data from one activity to another.

Android NFC always calling onCreate when app is closed [duplicate]

This question already has answers here:
How to use NFC ACTIONS
(5 answers)
Closed 3 years ago.
I made app that detects nfc tag. All work fine, when my app is closed and i scan the nfc tag with my phone it shows me an activity have onCreate() method , when i scan again for the 2nd time it works , i dont know if im wrong in the lifecycle of the app or that i missed something in my code?
when i open app , scanning is working : 1st photo
when app is closed 2nd photo : from 2nd photo but in the 2nd scan it works
this is my code
public class NfcActivity extends AppCompatActivity {
private static final String TAG = "NfcActivity";
private NfcAdapter mNfcAdapter;
private TextView mTextView;
PendingIntent pendingIntent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nfc);
mTextView = findViewById(R.id.tv_nfc_detail);
mNfcAdapter = NfcAdapter.getDefaultAdapter(getApplicationContext());
if (mNfcAdapter == null) {
Toast.makeText(this, "Cet appareil ne supporte pas nfc", Toast.LENGTH_SHORT).show();
finish();
return;
}
if (!mNfcAdapter.isEnabled()) {
startActivity(new Intent("android.settings.NFC_SETTINGS"));
Toast.makeText(this, "Activer nfc", Toast.LENGTH_SHORT).show();
}
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
}
#Override
protected void onPause() {
super.onPause();
mNfcAdapter.disableForegroundDispatch(this);
}
#Override
protected void onResume() {
super.onResume();
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter[] intentFilters = new IntentFilter[]{};
mNfcAdapter.enableForegroundDispatch(this, pendingIntent, intentFilters, null);
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
// if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
Tag iTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
mTextView.setText(TagReader.readTag(iTag, intent));
}
// }
}
}
<activity android:name=".Activities.NfcActivity" android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
</activity>
Edit: see full solution in related:
How to use NFC ACTIONS
You are only processing the intent the 2nd time round.
Add a new method based on your current onNewIntent() method like this:
private void onNewNfcTag(Intent intent) {
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())
|| NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
Tag iTag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
mTextView.setText(TagReader.readTag(iTag, intent));
}
}
Change your onNewIntent() to call this new method:
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
onNewNfcTag(intent);
}
Call this same method from onCreate() with the intent from getIntent():
#Override
protected void onCreate(Bundle savedInstanceState) {
// .... your code already here
onNewNfcTag(getIntent());
}

android: Making multiple phone calls from android app [duplicate]

I am launching an activity to make a phone call, but when I pressed the 'end call' button, it does not go back to my activity. Can you please tell me how can I launch a call activity which comes back to me when 'End call' button is pressed? This is how I'm making the phone call:
String url = "tel:3334444";
Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
use a PhoneStateListener to see when the call is ended. you will most likely need to trigger the listener actions to wait for a the call to start (wait until changed from PHONE_STATE_OFFHOOK to PHONE_STATE_IDLE again) and then write some code to bring your app back up on the IDLE state.
you may need to run the listener in a service to ensure it stays up and your app is restarted. some example code:
EndCallListener callListener = new EndCallListener();
TelephonyManager mTM = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
mTM.listen(callListener, PhoneStateListener.LISTEN_CALL_STATE);
Listener definition:
private class EndCallListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
if(TelephonyManager.CALL_STATE_RINGING == state) {
Log.i(LOG_TAG, "RINGING, number: " + incomingNumber);
}
if(TelephonyManager.CALL_STATE_OFFHOOK == state) {
//wait for phone to go offhook (probably set a boolean flag) so you know your app initiated the call.
Log.i(LOG_TAG, "OFFHOOK");
}
if(TelephonyManager.CALL_STATE_IDLE == state) {
//when this state occurs, and your flag is set, restart your app
Log.i(LOG_TAG, "IDLE");
}
}
}
In your Manifest.xml file add the following permission:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
This is regarding the question asked by Starter.
The problem with your code is that you are not passing the number properly.
The code should be:
private OnClickListener next = new OnClickListener() {
public void onClick(View v) {
EditText num=(EditText)findViewById(R.id.EditText01);
String number = "tel:" + num.getText().toString().trim();
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse(number));
startActivity(callIntent);
}
};
Do not forget to add the permission in manifest file.
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
or
<uses-permission android:name="android.permission.CALL_PRIVILEGED"></uses-permission>
for emergency number in case DIAL is used.
We had the same problem and managed to solve it by using a PhoneStateListener to identify when the call ends, but additionally we had to finish() the original activity before starting it again with startActivity, otherwise the call log would be in front of it.
I found the EndCallListener the most functional example, to get the behaviour described (finish(), call, restart) I added a few SharedPreferences so the Listener had a reference to manage this behaviour.
My OnClick, initialise and EndCallListener only respond to calls from app. Other calls ignored.
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
public class EndCallListener extends PhoneStateListener {
private String TAG ="EndCallListener";
private int LAUNCHED = -1;
SharedPreferences prefs = PreferenceManager
.getDefaultSharedPreferences(
myActivity.mApp.getBaseContext());
SharedPreferences.Editor _ed = prefs.edit();
#Override
public void onCallStateChanged(int state, String incomingNumber) {
String _prefKey = myActivity.mApp
.getResources().getString(R.string.last_phone_call_state_key),
_bPartyNumber = myActivity.mApp
.getResources().getString(R.string.last_phone_call_bparty_key);
int mLastCallState = prefs.getInt(_prefKey, LAUNCHED);
//Save current call sate for next call
_ed.putInt(_prefKey,state);
_ed.commit();
if(TelephonyManager.CALL_STATE_RINGING == state) {
Log.i(TAG, " >> RINGING, number: " + incomingNumber);
}
if(TelephonyManager.CALL_STATE_IDLE == state && mLastCallState != LAUNCHED ) {
//when this state occurs, and your flag is set, restart your app
if (incomingNumber.equals(_bPartyNumber) == true) {
//Call relates to last app initiated call
Intent _startMyActivity =
myActivity.mApp
.getPackageManager()
.getLaunchIntentForPackage(
myActivity.mApp.getResources()
.getString(R.string.figjam_package_path));
_startMyActivity.setAction(
myActivity.mApp.getResources()
.getString(R.string.main_show_phone_call_list));
myActivity.mApp
.startActivity(_startMyActivity);
Log.i(TAG, "IDLE >> Starting MyActivity with intent");
}
else
Log.i(TAG, "IDLE after calling "+incomingNumber);
}
}
}
add these to strings.xml
<string name="main_show_phone_call_list">android.intent.action.SHOW_PHONE_CALL_LIST</string>
<string name="last_phone_call_state_key">activityLpcsKey</string>
<string name="last_phone_call_bparty_key">activityLpbpKey</string>
and something like this in your Manifest if you need to return to the look and feel before the call
<activity android:label="#string/app_name" android:name="com.myPackage.myActivity"
android:windowSoftInputMode="stateHidden"
android:configChanges="keyboardHidden" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.SHOW_PHONE_CALL_LIST" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
and put these in your 'myActivity'
public static Activity mApp=null; //Before onCreate()
...
onCreate( ... ) {
...
if (mApp == null) mApp = this; //Links your resources to other classes
...
//Test if we've been called to show phone call list
Intent _outcome = getIntent();
String _phoneCallAction = mApp.getResources().getString(R.string.main_show_phone_call_list);
String _reqAction = _outcome.getAction();//Can be null when no intent involved
//Decide if we return to the Phone Call List view
if (_reqAction != null &&_reqAction.equals(_phoneCallAction) == true) {
//DO something to return to look and feel
}
...
myListView.setOnItemClickListener(new OnItemClickListener() { //Act on item when selected
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
myListView.moveToPosition(position);
String _bPartyNumber = "tel:"+myListView.getString(myListView.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
//Provide an initial state for the listener to access.
initialiseCallStatePreferences(_bPartyNumber);
//Setup the listener so we can restart myActivity
EndCallListener _callListener = new EndCallListener();
TelephonyManager _TM = (TelephonyManager)mApp.getSystemService(Context.TELEPHONY_SERVICE);
_TM.listen(_callListener, PhoneStateListener.LISTEN_CALL_STATE);
Intent _makeCall = new Intent(Intent.ACTION_CALL, Uri.parse(_bPartyNumber));
_makeCall.setComponent(new ComponentName("com.android.phone","com.android.phone.OutgoingCallBroadcaster"));
startActivity(_makeCall);
finish();
//Wait for call to enter the IDLE state and then we will be recalled by _callListener
}
});
}//end of onCreate()
use this to initilaise the behaviour for your onClick in myActivity e.g. after onCreate()
private void initialiseCallStatePreferences(String _BParty) {
final int LAUNCHED = -1;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
mApp.getBaseContext());
SharedPreferences.Editor _ed = prefs.edit();
String _prefKey = mApp.getString(R.string.last_phone_call_state_key),
_bPartyKey = mApp.getString(R.string.last_phone_call_bparty_key);
//Save default call state before next call
_ed.putInt(_prefKey,LAUNCHED);
_ed.putString(_bPartyKey,_BParty);
_ed.commit();
}
You should find that clicking your list of phone numbers finishes your activty, makes the call to the number and returns to your activty when the call ends.
Making a call from outside your app while it's still around won't restart your activty (unless it's the same as the last BParty number called).
:)
you can use startActivityForResult()
This is solution from my point of view:
ok.setOnClickListener(this);
#Override
public void onClick(View view) {
if(view == ok){
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + num));
activity.startActivity(intent);
}
Of course in Activity (class) definition you have to implement View.OnClickListener .
Here is my example, first the user gets to write in the number he/she wants to dial and then presses a call button and gets directed to the phone. After call cancelation the user gets sent back to the application. In order to this the button needs to have a onClick method ('makePhoneCall' in this example) in the xml. You also need to register the permission in the manifest.
Manifest
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Activity
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
public class PhoneCall extends Activity {
EditText phoneTo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_phone_call);
phoneTo = (EditText) findViewById(R.id.phoneNumber);
}
public void makePhoneCall(View view) {
try {
String number = phoneTo.getText().toString();
Intent phoneIntent = new Intent(Intent.ACTION_CALL);
phoneIntent.setData(Uri.parse("tel:"+ number));
startActivity(phoneIntent);
} catch (android.content.ActivityNotFoundException ex) {
Toast.makeText(PhoneCall.this,
"Call failed, please try again later!", Toast.LENGTH_SHORT).show();
}
}
}
XML
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="phone"
android:ems="10"
android:id="#+id/phoneNumber"
android:layout_marginTop="67dp"
android:layout_below="#+id/textView"
android:layout_centerHorizontal="true" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Call"
android:id="#+id/makePhoneCall"
android:onClick="makePhoneCall"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" />
#Override
public void onClick(View view) {
Intent phoneIntent = new Intent(Intent.ACTION_CALL);
phoneIntent.setData(Uri.parse("tel:91-000-000-0000"));
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
return;
}
startActivity(phoneIntent);
}
If you are going to use a listener you will need to add this permission to the manifest as well.
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
Inside PhoneStateListener after seeing the call is finished better use:
Intent intent = new Intent(CallDispatcherActivity.this, CallDispatcherActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
Where CallDispatcherActivity is the activity where the user has launched a call (to a taxi service dispatcher, in my case). This just removes Android telephony app from the top, the user gets back instead of ugly code I saw here.
To return to your Activity, you will need to listen to TelephonyStates. On that listener you can send an Intent to re-open your Activity once the phone is idle.
At least thats how I will do it.
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:"+number));
startActivity(callIntent);
**Add permission :**
<uses-permission android:name="android.permission.CALL_PHONE" />
Try using:
finish();
at the end of activity. It will redirect you to your previous activity.
When PhoneStateListener is used, one need to make sure PHONE_STATE_IDLE following a PHONE_STATE_OFFHOOK is used to trigger the action to be done after the call. If the trigger happens upon seeing PHONE_STATE_IDLE, you will end up doing it before the call. Because you will see the state change PHONE_STATE_IDLE -> PHONE_STATE_OFFHOOK -> PHONE_STATE_IDLE.
// in setonclicklistener put this code:
EditText et_number=(EditText)findViewById(R.id.id_of_edittext);
String my_number = et_number.getText().toString().trim();
Intent callIntent = new Intent(Intent.ACTION_CALL, Uri.parse(my_number));
startActivity(callIntent);
// give permission for call in manifest:
<uses-permission android:name="android.permission.CALL_PHONE"></uses-permission>
#Dmitri Novikov, FLAG_ACTIVITY_CLEAR_TOP clears any active instance on top of the new one. So, it may end the old instance before it completes the process.
Add this is your xml: android:autoLink="phone"
Steps:
1)Add the required permissions in the Manifest.xml file.
<!--For using the phone calls -->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!--For reading phone call state-->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
2)Create a listener for the phone state changes.
public class EndCallListener extends PhoneStateListener {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
if(TelephonyManager.CALL_STATE_RINGING == state) {
}
if(TelephonyManager.CALL_STATE_OFFHOOK == state) {
//wait for phone to go offhook (probably set a boolean flag) so you know your app initiated the call.
}
if(TelephonyManager.CALL_STATE_IDLE == state) {
//when this state occurs, and your flag is set, restart your app
Intent i = context.getPackageManager().getLaunchIntentForPackage(
context.getPackageName());
//For resuming the application from the previous state
i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//Uncomment the following if you want to restart the application instead of bring to front.
//i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);
}
}
}
3)Initialize the listener in your OnCreate
EndCallListener callListener = new EndCallListener();
TelephonyManager mTM = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE);
mTM.listen(callListener, PhoneStateListener.LISTEN_CALL_STATE);
but if you want to resume your application last state or to bring it back from the back stack, then replace FLAG_ACTIVITY_CLEAR_TOP with FLAG_ACTIVITY_SINGLE_TOP
Reference this Answer
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent .setData(Uri.parse("tel:+91-XXXXXXXXX"));
startActivity(callIntent );
When starting your call, it looks fine.
There is a difference between android 11+ and down in bringing your app to the front though.
Android 10 or less you need to start a new intent, android 11+ you simply use BringTaskToFront
In the call state IDLE:
if (Build.VERSION.SDK_INT >= 11) {
ActivityManager am = (ActivityManager) activity.getSystemService(Activity.ACTIVITY_SERVICE);
am.moveTaskToFront(MyActivity.MyActivityTaskId, ActivityManager.MOVE_TASK_WITH_HOME);
} else {
Intent intent = new Intent(activity, MyActivity.class);
activity.startActivity(intent);
}
I set the MyActivity.MyActivityTaskId when making the call on my activity like so, it this doesnt work, set this variable on the parent activity page of the page you want to get back to.
MyActivity.MyActivityTaskId = this.getTaskId();
MyActivityTaskId is a static variable on my activity class
public static int MyActivityTaskId = 0;
I hope this will work for you. I use the above code a bit differently, I open my app as soon as the call is answered sothat the user can see the details of the caller.
I have set some stuff in the AndroidManifest.xml as well:
/*Dont really know if this makes a difference*/
<activity android:name="MyActivity" android:taskAffinity="" android:launchMode="singleTask" />
and permissions:
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
Please ask questions if or when you get stuck.
To call from app use simple intent for call, after that if you want to listen the call status then use below code
1] implement this in your class -
implements AudioManager.OnAudioFocusChangeListener
2] Add below code with overridden method
AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE); //public static final String AUDIO_SERVICE = "audio";
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
assert audioManager != null;
audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
#Override
public void onAudioFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS: {
//here you can pause your playing audio
break;
}
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: {
//here you can pause your playing audio
break;
}
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: {
break;
}
case AudioManager.AUDIOFOCUS_GAIN: {
//here you will return back to the activity when your call is finsihed
//resume your audio here
break;
}
}
}
3]Add this
#Override
public void onDestroy() {
audioManager.abandonAudioFocus(this);
}

Android NFC Reader in MVP - onNewIntent not firing

I have a working NFC reader/writer code. Using the same code, I added the reader function in another app which is following MVP architecture.
The activity is named NFCReaderActivity. A separate NFC class is created (NFCReader), which implements Sensor interface.
The app is supposed to work both in the foreground and launch showing the NFC tag info. The launch part is working fine and app launches and reads the tag and shows its content.
However, in the foreground, on scanning, it does nothing. I only hear the scan beep but no onNewIntent is firing.
Below are the log entries captured for foreground and launch actions. There is a difference in the class names:
When not launching
I/ActivityManager: START u0 {act=android.nfc.action.NDEF_DISCOVERED typ=application/com.abc.vi flg=0x14008000 cmp=com.abc.vi/.ui.reader.NFCReader (has extras)} from uid 10038 on display 0
When launching
I/ActivityManager: START u0 {act=android.nfc.action.NDEF_DISCOVERED typ=application/com.abc.vi cmp=com.abc.vi/.ui.reader.NFCReaderActivity (has extras)} from uid 1027 on display 0
Activity
onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "__onCreate__ " );
setContentView(R.layout.activity_nfc_reader);
VI.setNFCReaderActivityContext(this); //VI is the Application class
ButterKnife.bind(this);
presenter = new ReaderPresenter(this);
}
onNewIntent
#Override
public void onNewIntent(Intent intent) {
Log.i(TAG, "__onNewIntent__ " );
// onResume gets called after this to handle the intent
// setIntent(intent);
presenter.onNewIntent(intent);
}
onResume, onPause
#Override
protected void onResume() {
super.onResume();
Log.i(TAG, "__onResume__ " );
presenter.onResume();
}
#Override
protected void onPause() {
super.onPause();
Log.i(TAG, "__onPause__ " );
presenter.onPause();
}
Presenter
ReaderPresenter(ReaderContract.View view) {
this.view = view;
initSensor();
}
#Override
public void initSensor() {
nfcReader = new NFCReader(VI.getNFCReaderActivityContext(), this); //VI is the Application class
}
#Override
public void onNewIntent(Intent intent) {
nfcReader.resolveIntent(intent);
}
#Override
public void onResume() {
nfcReader.onResume();
}
#Override
public void onPause() {
nfcReader.onPause();
}
#Override
public void onDestroy() {
speech.onDestroy();
}
NFCReader
public class NFCReader implements Sensors {
private static final String TAG = NFCReader.class.getSimpleName();
private NfcAdapter nfcAdapter;
private PendingIntent nfcPendingIntent;
private NFCReaderActivity activity;
private ReaderPresenter presenter;
NFCReader(NFCReaderActivity nfcReaderActivity, ReaderPresenter readerPresenter) {
this.activity = nfcReaderActivity;
this.presenter = readerPresenter;
init();
}
#Override
public void init() {
//Initialize NFC adapter
nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
nfcPendingIntent = PendingIntent.getActivity(activity, 0, new Intent(activity,
getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP), 0);
}
public void onResume() {
if (nfcAdapter != null) {
nfcAdapter.enableForegroundDispatch(activity, nfcPendingIntent, null, null);
// if NFC not enabled
if (!nfcAdapter.isEnabled()) {
new AlertDialog.Builder(activity)
.setPositiveButton(activity.getString(R.string.update_setting_btn),
(dialog, which) -> {
Intent setNfc = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
activity.startActivity(setNfc);
})
.setOnCancelListener(
dialog -> activity.finish()
)
.create().show();
}
resolveIntent(activity.getIntent());
} else {
Toast.makeText(VI.getAppContext(),
activity.getString(R.string.error_no_nfc_found), Toast.LENGTH_LONG).show();
}
}
public void onPause() {
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(activity);
}
}
public void resolveIntent(Intent intent){
Log.i(TAG, "__resolveIntent__");
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
NdefMessage[] messages = null;
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
messages = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
messages[i] = (NdefMessage) rawMsgs[i];
}
}
if ((messages != null ? messages[0] : null) != null) {
StringBuilder result = new StringBuilder();
byte[] payload = messages[0].getRecords()[0].getPayload();
for (byte aPayload : payload) {
result.append((char) aPayload);
}
Log.i(TAG,"Decoded --> "+result.toString());
presenter.getData(result.toString());
}
}
}
}
Manifest
<activity android:name=".ui.reader.NFCReaderActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="#string/mime_type" />
</intent-filter>
</activity>
UPDATE
I moved all the code from NFCReader class to NFCReaderActivity and both foreground and launch modes are working. The issue is with MVP architecture. How to convert it back to MVP?
You seem to register the pending intent for the wrong (actually an invalid) component (not your activity class). The reason is that when you create the PendingIntent that you assign to nfcPendingIntent, you use getClass() to obtain the class of the NFCReader instance. Instead you would need to use activity.getClass() to obtain the class of your activity component.

Categories