How to stop NfcAdapter.ACTION_NDEF_DISCOVERED becoming true when device boots? - java

My application starts when the device boots (Nexus 7). When my device boots if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) becomes true. Then the code inside the if crashes because the intent was not really a ACTION_NDEF_DISCOVERED but a boot.
I can put a try catch and then it won't crash. However then NFC won't work. To get NFC to work the application must be closed and reopened.
Is there a way to check the if(action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED)) but have it ignore the booting? It is really annoying because the if is checking for NFC not boot.
#Override
public void onResume()
{
super.onResume();
// NFC code.
Intent intent = getIntent();
String action = intent.getAction();
PendingIntent pi = this.createPendingResult(0x00A, new Intent(), 0);
nfcAdapter.enableForegroundDispatch(this, pi, null, null);
try
{
// NFC transfer. Receiving message here.
if(action != null && action.equals(NfcAdapter.ACTION_NDEF_DISCOVERED))
{
Parcelable[] parcelables = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage inNdefMessage = (NdefMessage) parcelables[0];
NdefRecord[] inNdefRecords = inNdefMessage.getRecords();
NdefRecord NdefRecord_0 = inNdefRecords[0];
String inMsg = new String(NdefRecord_0.getPayload());
Toast.makeText(getApplicationContext(), "Toasty: "+inMsg + action.toString(), Toast.LENGTH_LONG).show();
textInfo.setText(inMsg);
}
}
catch(Exception e)
{
Log.e("NFC", e.getMessage());
}
}
This is the code that checks for BOOTING.
public class BootManager extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED))
{
Intent i = new Intent(context, Login_Activity.class);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
}
In the Login_Activity, Could this be altering the intent?
#Override
protected void onNewIntent(Intent intent)
{
setIntent(intent);
}

Related

How to transfer Geofence transition values from broadcast receiver to my main activity

I'm working on a feature in my app that moves a marker to a certain latlng when the user exits the geofence.
Send the broadcast received data to any activity
public class GeofenceBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "GeofenceBroadcastReceiver";
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
// Toast.makeText(context, "Geofence triggered...", Toast.LENGTH_SHORT).show();
NotificationHelper notificationHelper = new NotificationHelper(context);
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
Log.d(TAG, "onReceive: Error receiving geofence event...");
return;
}
List<Geofence> geofenceList = geofencingEvent.getTriggeringGeofences();
for (Geofence geofence: geofenceList) {
Log.d(TAG, "onReceive: " + geofence.getRequestId());
}
// Location location = geofencingEvent.getTriggeringLocation();
int transitionType = geofencingEvent.getGeofenceTransition();
switch (transitionType) {
case Geofence.GEOFENCE_TRANSITION_ENTER:
Toast.makeText(context, "You are near the drop off location.", Toast.LENGTH_SHORT).show();
notificationHelper.sendHighPriorityNotification("You are near the drop off location", "", Home.class);
break;
case Geofence.GEOFENCE_TRANSITION_DWELL:
Toast.makeText(context, "Pickup/Drop off Location reached.", Toast.LENGTH_SHORT).show();
notificationHelper.sendHighPriorityNotification("Pickup/Drop off Location reached.", "", Home.class);
break;
case Geofence.GEOFENCE_TRANSITION_EXIT:
Toast.makeText(context, "Leaving Pickup/Drop off point.", Toast.LENGTH_SHORT).show();
notificationHelper.sendHighPriorityNotification("Leaving Pickup/Drop off point.", "", Home.class);
break;
}
}
}
I tried using intents but failed due to me being a beginner.
Using LocalBroadcastManager, we may transfer data from onReceive to another activity.
#Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
Intent intent = new Intent("broadCastName"); //new Intent(context, ReceiveText.class);
// Data you need to pass to another activity
intent .putExtra("message", extras.getString(Config.MESSAGE_KEY));
context.sendBroadcast(intent );
}

How to start new activity on nfc tag discovery?

I am writing a code which should return NFC tag value on the next activity ( page ) when NFC get detected during scan. What happens here is that when I launch the app for the first time it the first page shows for a fraction of a second and moves to second page directly ( activity ).
Here is the piece of code for the first activity ( which is just for asking user to tap to scan )
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
askPermissions();
mtxtViewNfcContent = (TextView) findViewById(R.id.text);
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
Toast.makeText(this, "No NFC", Toast.LENGTH_SHORT).show();
finish();
return;
}
else {
Intent in = new Intent(Main2Activity.this, MainActivity.class);
startActivity(in);
}
What I want is the the to show the first page at launch and when user tap to nfc scan, show the output on the next page ( MainActivity).
PS : I am new to android, please excuse with my codes.
what are you doing until now is to exit the app when the device doesnot support nfc or to start another activity when the device supports nfc.
you are actually not listening at all to any tag.
here you have two possibilities:
first : read an nfc tag in the first activity and then creat a new intent with and put the result of tag reading as extra bundel.
two : listen to tag existance in the first activity and then send the tag to second one and read it in the second activity.
I would prefer the first secinario.
on firstActivity:
public class MainActivity extends AppCompatActivity {
private PendingIntent pendingIntent;
private IntentFilter[] writeTagFilters;
private NfcAdapter nfcAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTheme(R.style.AppTheme);
setContentView(R.layout.activity_main);
nfcAdapter = NfcAdapter.getDefaultAdapter(this);
if (nfcAdapter == null) {
Toast.makeText(this, "No NFC", Toast.LENGTH_SHORT).show();
finish();
return;
}
setForeground();
}
private void setForeground() {
pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
writeTagFilters = new IntentFilter[]{tagDetected};
}
#Override
protected void onResume() {
super.onResume();
if (nfcAdapter != null) {
nfcAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
}
processNfcTag(getIntent());
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
#Override
protected void onPause() {
super.onPause();
if (nfcAdapter != null) {
nfcAdapter.disableForegroundDispatch(this);
}
}
private void processNfcTag(Intent intent) {
//TODO: here you should to check if this intent is an NFC Intent, in case it is an nfc intent you could read it according of tag tech you have
// for example MifareUltralight.
MifareUltralight mfu = MifareUltralight.get(intent.getParcelableExtra(NfcAdapter.EXTRA_TAG));
try {
mfu.connect();
byte [] bytes = mfu.readPages(pageNumber);
mfu.close();
} catch (IOException e) {
e.printStackTrace();
}
// then you could get this bytes and send it to the other activity
}
please check this link to know how to send data between activities.
p.s: you should to check the code I have wrote it quickly.
You can use intent.putExtra (key,value) while calling the intent and use bundle on the result activity to fetch the variable data
use this while calling the intent
`Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("some_key", value);
intent.putExtra("some_other_key", "a value");
startActivity(intent);`
use this on result activity
`Bundle bundle = getIntent().getExtras();
int valueText = bundle.getInt("some_key");
String valueString = bundle.getString("some_other_key");
TextView textone =(TextView)findVeiwById(R.id.textone);
textone.setText(valueText);
TextView stringTextView = (TextView)FindViewById(R.id.stringTextView)
stringTextView.setText(valueString)`

onSensorChanged stops being called after 3 minutes of Locked screen

I am trying to develop a simple app which will record the user's activity (accelerometer values) on a txt or csv file.
My app consists of 2 java classes MainActivity and MyService. The MainActivity includes two buttons to start and stop the service and the required permissions. However, the onSensorChanged normally logs for the first 3 minutes after locking the phone (turning off the screen) and then stops logging. As soon as I open the screen the logd starts working again. Same behavior for the records in txt file. I found out that the app seems to be working excellent if I override the battery optimizations. However, I need the phone to also be working in doze mode to save some battery drain. Has anyone else had a similar issue?
Here is my Foreground Service:
public class MyService extends Service implements SensorEventListener {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
private static final String TAG = "MyService";
private Messenger messageHandler;
private SensorManager mSensorManager;
private Sensor mAccelerometer;
private Context mContext;
private PowerManager.WakeLock wakeLock = null;
//private HandlerThread mSensorThread;
//private Handler mHandler;
#SuppressLint("MissingPermission")
#Override
public void onCreate() {
super.onCreate();
Log.v("shake service startup", "registering for shake");
mContext = getApplicationContext();
//mHandler = new Handler(mSensorThread.getLooper());
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
mAccelerometer = mSensorManager
.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL);
PowerManager manager = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Wakelock :: TAG");
// Register our receiver for the ACTION_SCREEN_OFF action. This will make our receiver
// code be called whenever the phone enters standby mode.
//IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
//registerReceiver(mReceiver, filter);
}
/*
// BroadcastReceiver for handling ACTION_SCREEN_OFF.
public BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Check action just to be on the safe side.
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Log.v("shake mediator screen off","trying re-registration");
// Unregisters the listener and registers it again.
mSensorManager.unregisterListener(MyService.this);
mSensorManager.registerListener(MyService.this, mAccelerometer,
SensorManager.SENSOR_DELAY_NORMAL, mHandler);
}
}
};
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
return START_STICKY;
//stopSelf();
//return START_NOT_STICKY;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
"Foreground Service Channel",
NotificationManager.IMPORTANCE_DEFAULT
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
#Override
public void onDestroy() {
super.onDestroy();
if(mSensorManager != null){
//noinspection MissingPermission
mSensorManager.unregisterListener(MyService.this);
}
//unregisterReceiver(mReceiver);
try{
wakeLock.release();//always release before acquiring for safety just in case
}
catch(Exception e){
//probably already released
Log.e(TAG, e.getMessage());
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onSensorChanged(SensorEvent event) {
Log.d(TAG, "onSensorChanged: " + event.timestamp + " " + event.values[0] + " " + event.values[1] + " " + event.values[2]);
recordAccelValues(String.valueOf(event.timestamp), event.values[0] + " " + event.values[1] + " " + event.values[2]);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
private void recordAccelValues(String time, String accel_values) {
String record = time + " " + accel_values + "\n";
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
if (Build.VERSION.SDK_INT >= 23) {
File sdcard = Environment.getExternalStorageDirectory();
File dir = new File(sdcard.getAbsolutePath() + "/text/");
if(!dir.exists()) {
dir.mkdir();
}
File file = new File(dir, "dailyRecordsAccel.dat");
FileOutputStream os = null;
try {
os = new FileOutputStream(file, true);
os.write(record.getBytes());
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
As you can see in the code I tried several recommendations from other questions I found, like wakelock and Intent.ACTION_SCREEN_OFF but they didn't seem to work.
Accelerometer stops delivering samples when the screen is off on Droid/Nexus One even with a WakeLock
The only one way to keep alive your service it's to avoid battery optimization for your application. Which is possible within two ways below. Please note! In both cases you will keep device alive, which means that device will never sleep (enter doze states obviously). It's whole point of device sleep, to avoid pending work of background services like yours.
Using Android WakeLocks, For ex. below.
val wakeLock: PowerManager.WakeLock =
(getSystemService(Context.POWER_SERVICE) as PowerManager).run {
newWakeLock(PowerManager. FULL_WAKE_LOCK, "MyApp::MyWakelockTag").apply {
acquire()
}
}
Changing setting to avoid battery optimization for specific app. As you mentioned in your question.
It is normal behavior. Android delete all proceses to save power. If you want do a job then ask user to keep screen on, else you can use AlarmManager only to call a Service (Intent, Reciver) do "small job" and go to sleep again.

Should i use StartService or StartForegroundService for API >= 26?

I'm a bit confused because i read some posts where i'm supposed too use ContextCompat.StartForegroundService(); if the API is >= 26.
Now I still just use StartService and it works even though i'm supposed to get an IllegalStateException on an API >= 26 ( current api on phone is 27) according to this post.
https://medium.com/mindorks/mastering-android-service-of-2018-a4a1df5ed5a6
I know Service is an old concept. Let me assure you we will not discuss the basics and we will learn the recent changes made to the service layer in Android 8.0+, we will solve the mystery of famous IllegalStateException and RemoteServiceException. This article is not a conventional way of understanding services, hang tight till you can.
So my question is if i should change startForeGroundService or just keep startService for API >=26?
My Class that handles my Service connection:
/**This establishes the connection to the MediaPlayerService. */
public static ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaPlayerService.MusicBinder binder = (MediaPlayerService.MusicBinder)service;
mediaPlayerService = binder.getService();
mediaPlayerService.musicBound = true;
}
#Override
public void onServiceDisconnected(ComponentName name) {
mediaPlayerService.musicBound = false;
}
};
/**This is called to start the MediaPlayerService. */
private static Intent mediaPlayerServiceIntent = null;
public static void startMusicService(Context c) {
/*mediaPlayerServiceIntent binds our connection to the MediaPlayerService. */
mediaPlayerServiceIntent = new Intent(c, MediaPlayerService.class);
c.bindService(mediaPlayerServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
c.startService(mediaPlayerServiceIntent);
mServiceIsActive = true;
}
/**This is called to stop the MediaPlayerService. (onDestroy) */
public static void stopMusicService(Context c) {
if (mediaPlayerServiceIntent == null)
return;
c.unbindService(serviceConnection);
c.stopService(mediaPlayerServiceIntent);
mediaPlayerServiceIntent = null;
mediaPlayerService = null;
}
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Main.startMusicService(getApplicationContext());
}
startService will not work for api >=26
You can change your service to foreground service with help of following code. It will show the notification.
private void runAsForeground(){
Intent notificationIntent = new Intent(this, MediaPlayerService.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this, 0,
notificationIntent, Intent.FLAG_ACTIVITY_NEW_TASK);
Notification notification=new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentText(getString(R.string.isRecording))
.setContentIntent(pendingIntent).build();
startForeground(NOTIFICATION_ID, notification);
}
for more reference - https://android-developers.googleblog.com/2018/12/effective-foreground-services-on-android_11.html
https://developer.android.com/guide/components/services
another way (not recommended.. target sdk must be 26 or less)
public static void startService(Context context, Class className) {
try {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N) {
Intent restartServiceIntent = new Intent(context, className);
restartServiceIntent.setPackage(context.getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(context, 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmService != null) {
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 500,
restartServicePendingIntent);
}
} else {
Intent i = new Intent(context, className);
context.startService(i);
}
} catch (Exception e) {
MyLog.e(TAG, "startService: ", e);
}
}
Call by
startService(context,MediaPlayerService.class);

Android: Broadcast Receiver does not receive BluetoothDevice.ACTION_ACL_CONNECTED on restarting the application

I want my app to auto-connect to already connected bluetooth device on restarting the app. Below is procedure I am performing:-
[Initially] Bluetooth device is 'ON': Then on starting the app.
[Behavior]--> Bluetooth device gets paired and connected successfully ( Intent 'ACTION_ACL_CONNECTED' is received)
Bluetooth device is 'ON': Closed the app, then started the app again.
[Behavior]--> Even though it is connected as displayed on Bluetooth setting, and Broadcast Receiver does not receive Intent 'ACTION_ACL_CONNECTED'.
Note:- On closing the app, it does not disconnect the bluetooth connection.
So, on successful connection app straightaway goes to the HomeScreen. Otherwise, the app goes to a screen having button that takes it to Bluetooth setting(onClickListener present in the code below)
I am new to android development, so I really don't know where am I going wrong. I looked up the solutions for similar issues and applied them, but to no effect.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED);
registerReceiver(mReceiver, filter);
IntentFilter filter1 = new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED);
this.registerReceiver(mReceiver, filter1);
m_app = (BtApp) getApplication();
imagebt = (ImageView) this.findViewById(R.id.imagebt);
imagebt.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
final Toast tag = Toast.makeText(getApplicationContext(), "Connect to device", Toast.LENGTH_LONG);
tag.show();
new CountDownTimer(1000, 1000)
{
public void onTick(long millisUntilFinished) {tag.show();}
public void onFinish() {
//tag.show();
}
}.start();
if(mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()){
mBluetoothAdapter.startDiscovery();
}
Intent intentBluetooth = new Intent();
intentBluetooth.setAction(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS);
startActivity(intentBluetooth);
}
});
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ( BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
m_app.m_main.setupCommPort();
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
m_app.m_device = device;
isconnected = true;
new Timer().schedule(new TimerTask() {
#Override
public void run() {
if ( m_app.m_main.m_BtService != null && m_app.m_main.m_BtService.getState() != BluetoothRFCommService.STATE_CONNECTED ) {
m_app.m_main.m_BtService.connect(device, false);
}
}
}, 3500);
} else if ( BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action) ) {
isconnected = false;
m_app.m_main.tabHost.setCurrentTab(0);
}
}
};
#Override
protected void onStop()
{
unregisterReceiver(mReceiver);
super.onStop();
}
You won't get BluetoothDevice.ACTION_ACL_CONNECTED event since the device is still connected. The event is fired only on changing of device state from disconnected to connected.
You have 2 options.
You can put your BroadcastReceiver with BluetoothDevice.ACTION_ACL_CONNECTED and BluetoothDevice.ACTION_ACL_DISCONNECTED filters into the Service and track the device connection state in the background. On your app startup you can ask the service to give you the current state of the device.
You can check if some of the Bluetooth profiles contains your device name in the list of connected devices.
For API 18+ you can use BluetoothManager#getConnectedDevices() for API below 18 you can use the following snippet (for each Bluetooth profile)
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
for (BluetoothDevice device : proxy.getConnectedDevices()) {
if (device.getName().contains("DEVICE_NAME")) {
deviceConnected = true;
}
}
if (!deviceConnected) {
Toast.makeText(getActivity(), "DEVICE NOT CONNECTED", Toast.LENGTH_SHORT).show();
}
mBluetoothAdapter.closeProfileProxy(profile, proxy);
}
public void onServiceDisconnected(int profile) {
// TODO
}
};
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.A2DP);

Categories

Resources