I've been working on an Android NFC application on Android Studio, and I've hit a major roadblock. Basically, everytime my app detects a new NDEF NFC Tag, a new intent is created, and MainActivity seems to be reloaded, which results in onPause(), onCreate and onResume() being called.
The problem is, the app functions on a "session" system. The user scans a tag, logs in on a remote server, and the boolean sessionOpen is set to true. After that, the user is supposed to scan a different NFC tag, which would re-direct them on another page, while still being logged in (using a cookie) and the session still being opened.
Now, since onCreate() is called after a new NFC intent, it resets everything back to 0, which makes it impossible for me to keep track of their session., or anything for that matter.
I've tried overriding onSaveInstanceState and onRestoreInstanceState, but to no avail. Is there a way to prevent the NFC Intent from reloading the MainActivity, or from calling onCreate()? Or is there a way to restore variables from an older save state?
Here is my MainActivity.Java : (bits not relevant as well as some declarations have been removed)
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Parcelable;
import android.provider.Settings;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.KeyEvent;
import android.webkit.CookieSyncManager;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.webkit.CookieManager;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import com.google.android.material.snackbar.Snackbar;
import okhttp3.Cookie;
public class MainActivity extends AppCompatActivity {
public static final String sessionSavedState = "";
public boolean sessionOpen;
WebView web;
TextView usernameview;
TextView uridetector;
protected LocationManager locationManager;
protected LocationListener locationListener;
public String NFCid = "";
public static final String Error_Detected = "No NFC Tag Detected";
NfcAdapter nfcAdapter;
PendingIntent pendingIntent;
IntentFilter writingTagFilters[];
boolean writeMode;
Tag myTag;
Context context;
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
session = findViewById(R.id.sessionview);
time = findViewById(R.id.sessiontime);
user = findViewById(R.id.userview);
Memo = findViewById(R.id.multiline);
Memo.setMovementMethod(new ScrollingMovementMethod());
Memo.setText("Init\n");
tag_status = findViewById(R.id.tagstatus);
web = findViewById(R.id.WebView1);
CookieManager.getInstance().removeAllCookies(null);
WebSettings webSettings = web.getSettings();
webSettings.setJavaScriptEnabled(true);
web.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
web.setWebViewClient(new Callback());
web.setWebViewClient(new WebViewClient(){
@Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
if (CookieManager.getInstance().getCookie(web.getUrl()).contains("dc-auth"))
{
AuthCount = AuthCount + 1;
}
Memo.setText(Memo.getText() + "1) cookie before: " + CookieManager.getInstance().getCookie(web.getUrl()) + "\n");
Memo.setText(Memo.getText() + "session status : " + String.valueOf(sessionOpen) + "\n");
if (!sessionOpen)
{
if (CookieManager.getInstance().getCookie(web.getUrl()).contains("dc-auth") && AuthCount >= 2)
{
super.doUpdateVisitedHistory(view, url, isReload);
Memo.setText(Memo.getText() + "1) session opened\n");
sessionOpen = true;
tag_status.setImageResource(R.drawable.tagetape_wait);
}
else
{
super.doUpdateVisitedHistory(view, url, isReload);
}
}
else
{
Memo.setText(Memo.getText() + "1) passage\n");
super.doUpdateVisitedHistory(view, url, isReload);
}
Memo.setText(Memo.getText() + "1.1) passage\n");
String currentUrl = view.getUrl();
}
});
web.loadUrl("adress.com");
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationListener = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
}
};
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] {
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.INTERNET
}, 10);
return;
}
locationManager.requestLocationUpdates(locationManager.GPS_PROVIDER, 5000, 0, locationListener);
Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
longitudeString = String.valueOf(location.getLongitude());
latitudeString = String.valueOf(location.getLatitude());
if (longitudeString.isEmpty() && latitudeString.isEmpty())
{
LocationChange = false;
}
else{
LocationChange = true;
}
context = this;
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
Toast.makeText(this, "This device does not support NFC", Toast.LENGTH_SHORT).show();
finish();
}
readFromIntent(getIntent());
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), 0);
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
writingTagFilters = new IntentFilter[] { tagDetected };
}
public void readFromIntent(Intent intent) {
String action = intent.getAction();
Uri uri = intent.getData();
if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action) || NfcAdapter.ACTION_TECH_DISCOVERED.equals(action) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Parcelable[] rawMsgs = getIntent().getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
byte[] uid = tag.getId(); //This is the Unique IDentifier of the card
//tag_id.setText(bytesToHex(uid)); //recuperation du payload NFC
NFCid = bytesToHex(uid);
NdefMessage[] msgs = null;
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
//uridetector.setText(msgs[i].toString());
}
}
buildTagViews(msgs);
}
}
@SuppressLint("SetTextI18n")
private void buildTagViews(NdefMessage[] msgs) {
if (msgs == null || msgs.length == 0) return;
Uri ndef_uri;
String text = "";
String uri = "";
String ID = "";
String cookie = "";
byte[] payload = msgs[0].getRecords()[0].getPayload();
byte[] URIpayload = msgs[0].getRecords()[1].getPayload();
int languageCodeLength = payload[0] & 0063; //get the language code, for e.x : "en"
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16"; //verify encoding
try {
text = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
uri = new String(URIpayload, languageCodeLength - 1, URIpayload.length - languageCodeLength + 1, textEncoding);
if (text.contains(";") && text.contains("user")) {
textList = text.split(";");
user.setText(textList[1]);
session.setText("session de " + textList[1]);
if (textList[2].contains("1")) { //DEBUG MODE
cookie = CookieManager.getInstance().getCookie(web.getUrl());
Memo.setText(Memo.getText() + "cookie before: " + cookie + "\n");
}
tag_status.setImageResource(R.drawable.tagdetected);
if (uri.contains("user") && uri.contains("adress.com")) {
if (LocationChange = false)
{
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "Service de geo-location en cours d'initialisation. Veuillez patienter...", Snackbar.LENGTH_LONG);
snackbar.show();
}
else
{
uri = uri + "arugments"
web.loadUrl("https://" + uri);
}
}
} else if (text.contains("mobile")) {
tag_status.setImageResource(R.drawable.tagdetected);
if (!sessionOpen) {
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "Aucune session ouverte. Veuillez scanner un tag utilisateur et/ou vous logger.", Snackbar.LENGTH_LONG);
snackbar.show();
}
else
{
tag_status.setImageResource(R.drawable.tagetape_wait);
}
if (uri.contains("stop") && uri.contains("adress.com"))
{
cookie = CookieManager.getInstance().getCookie(web.getUrl());
Memo.setText(Memo.getText() + "cookie after: " + cookie + "\n");
if (sessionOpen) {
uri = uri + "?tagid=" + NFCid + "&deviceid=" + getDeviceId2(context);
web.loadUrl("https://" + uri);
}
}
else
{
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "Invalid NFC URL. ", Snackbar.LENGTH_LONG);
snackbar.show();
}
} else {
Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "Tag NFC Invalide.", Snackbar.LENGTH_LONG);
snackbar.show();
}
} catch (UnsupportedEncodingException e) {
Log.e("UnsupportedEncoding", e.toString());
}
int prefixCode = payload[0] & 0x0FF;
if (prefixCode >= URI_PREFIX.length) prefixCode = 0;
String reducedUri = new String(payload, 1, payload.length - 1, Charset.forName("UTF-8"));
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putBoolean(sessionSavedState, sessionOpen);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// Restore UI state from the savedInstanceState.
// This bundle has also been passed to onCreate.
sessionOpen = savedInstanceState.getBoolean(sessionSavedState);
}
@Override
protected void onDestroy(){
super.onDestroy();
// Clear all the cookies
CookieManager.getInstance().removeAllCookies(null);
CookieManager.getInstance().flush();
web.clearCache(true);
web.clearFormData();
web.clearHistory();
web.clearSslPreferences();
}
public void clearCookiesAndCache(Context context){
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
}
public static String getDeviceId2(Context context) {
String androidId = Settings.Secure.getString(
context.getContentResolver(), Settings.Secure.ANDROID_ID);
return androidId;
}
private class Callback extends WebViewClient {
@Override
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
return false; //do not override loaded page
}
}
//timeout - disconnect methods
@Override
protected void onResume()
{
super.onResume();
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
if (sessionOpen) {
mCountDown.start();
}
}
@Override
protected void onPause()
{
super.onPause();
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
nfcAdapter.disableForegroundDispatch(this);
mCountDown.cancel();
}
@Override
public void onUserInteraction()
{
super.onUserInteraction();
// user interact cancel the timer and restart to countdown to next interaction
mCountDown.cancel();
if (sessionOpen) {
mCountDown.start();
}
}
}