I have an application that uses NFC. When the Activity is created and enableForegroundDispatch() has been called, the NFC is scanned and accepted by my application. However, when the NFC is scanned DURING rotation (meaning the Activity has not been created yet, and enableForegroundDispatch() was not yet called), the default Android NFC scanner takes over displaying the "New tag collected" screen.
Is there any way to carry over the enableForegroundDispatch() even during device rotation? Or is there a way to temporarily "block" the default Android NFC capability when the device is being rotated?
Thanks
EDIT: The application only accepts the NFC scan when it is running. Scanning the NFC while the app is not running only displays a "New tag collected" screen
There's different possibilities:
Set screen orientation for your activity
<activity android:name=".MyActivity"
android:screenOrientation="portrait"
android:label="#string/app_name">
Tell android not restart activity when device rotation :
https://developer.android.com/guide/topics/resources/runtime-changes.html#HandlingTheChange
If your application doesn't need to update resources during a specific
configuration change and you have a performance limitation that
requires you to avoid the activity restart, then you can declare that
your activity handles the configuration change itself, which prevents
the system from restarting your activity.
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="#string/app_name">
UPDATE 1
If you use applying android:configChanges. You have to manage yourself screen rotation as explained in doc :
Now, when one of these configurations change, MyActivity does not
restart. Instead, the MyActivity receives a call to
onConfigurationChanged(). This method is passed a Configuration object
that specifies the new device configuration. By reading fields in the
Configuration, you can determine the new configuration and make
appropriate changes by updating the resources used in your interface.
At the time this method is called, your activity's Resources object is
updated to return resources based on the new configuration, so you can
easily reset elements of your UI without the system restarting your
activity.
Related
We are currently experiencing a weird Problem with new Android Devices. We finally updated from our Android 6. Handhelds to Android 10 and greater. Which helps us a lot but at the same time opened a real serious Problem.
We use our Handhelds for a lot different things but on of the main reasons is to identify and execute things on the discovery of a NFC Tag.
And we are experiencing that if the NFC discovery starts an activity switch it happens that instead of the new activity the screen ("New Tag Collected") pops up. The new Activity Starts in the Background. On Android 6 and before this worked fine. And we don't find anything related to it.
We think it has something to do with NFC Tag scanned -> Start Activity Switch -> NFC Adapter disabled -> Device Reads -> NFC Tag again -> New Activity started
Video to the Issue It opens new tag collected, and in the background the activity which should start
Android Device
Device: Zebra TC26BK
Android Version: 10
Kernel: 4.4.205-perf
MinSdk: 21
NFC Tag
Manufacturer: NXP
Model: NTAG213
Type: NFC Forum Type 2 TAG
Technology: NfcA, MifareUltralight,Ndef
Size: 144 bytes
Writeable: Yes (not used)
And the a test Repository with the code.
Repo
Thank you in advance if someone knows why this happens.
So my thoughts are because you are doing something you logically are not supposed to be doing you are confusing the system and now with the new OS and faster CPU's your unpredictable outcome is different than before.
So it looks like from the video the card is triggering the in built system NFC App to display the "Empty Tag" message.
Because you are enabling reader mode and foreground dispatch at the same type you are basically asking the system to do 2 things every time it sees a Tag
So again my thoughts on what is happening now in the System NFC service.
The Tag is detected.
The enableReaderMode callback starts the Second activity and this now becomes the foreground activity.
The NFC system service say "I now need to deliver an Intent to Main activity when it is in the foreground"
Oh Main activity is not in the foreground and the current foreground activity (The second activity) does not want to be delivered an Intent (as it does nothing with NFC enableForegroundDispatch).
Lets look for another App that wants to be started when this type of Tag is detected (Checks for Manifest entries)
There is no App that wants to be started, therefore the system NFC service defaults to staring the in built NFC App (which displays the empty Tag)
The fact in the video that when you press the back button the "SecondActivity" is there show that the enableReaderMode part has worked.
Previously
Due to a different timing because of a different CPU speed and more likely less CPU cores.
When the system NFC service handled the second NFC request to process the foreground dispatch, the "SecondActivity" was not running yet therefore it managed to deliver the Intent to "MainActivity" and generate 2 requests to the system to start the same activity (SecondActivity), the second of these requests would just bring SecondActivity to the foreground but would actually do nothing because the SecondActivity was already in the foreground.
Solution
Don't use enableForegroundDispatch and enableReaderMode at the same time in the same App, they logically do the same thing. I would recommend just using enableReaderMode as it is the better of the 2 API's
I also note that your code creates a bad IntentFilter which it does not use.
An ACTION_TECH_DISCOVERED IntentFilter does not take mimeType options.
e.g. the addDataType("*/*"); has no affect only ACTION_NDEF_DISCOVERED IntentFilters take mimeTypes
Also note that as you don't seem to be doing anything with NDEF data you probably want to add the FLAG_READER_SKIP_NDEF_CHECK flag to enableReaderMode, it just speeds things up.
Summary
The code is doing exactly what you asked it to do, but a different speed of code execution on a new device is leading to a different result.
I was creating an app that supports pip mode. I came across android:configChanges. What does it do?
In Android, when device configuration changes (such as users rotate the screen orientation, change language, etc..), the system will restart the running Activity (onDestroyed() is called, followed by onCreate()) to apply the new configuration.
If your application doesn't want this behavior to happen (maybe you don't want to restart the running activity or you want to apply the new configuration by yourself), then you can declare that the activity will handle the configuration by itself which preventing the system from restarting the activity.
To do that you go to the AndroidManifest.xml file, find the appropriate <activity> tag, add the android:configChanges attribute with a value that represents the configuration you want to handle.
Now when the configuration changes, the system does not restart your activity. Instead your activity will receive a call to onConfigurationChanged(Configuration) method along with the new configuration.
You should go to activity-element, read the "android:configChanges" section, you will understand it clearly. By the way, Android provides documentation about how to handle configuration changes in runtime here.
If my app crashes (reason is unimportant - could be hardware issue, out of memory, whatever), Android always starts my app on the last activity. The app is used by people visiting an office for appointments. When the customer arrives, reception unlocks the app using a code for that client and then the client proceeds through several activities. So, there's client context that is needed for all activities in the app except the initial unlock screen. As a result, the app needs to return to the start screen if the app crashes for any reason, but Android always returns to the last activity.
It seems like this should be really easy to do, but Android "helpfully" restarts the last activity used, which can't proceed without the client context. Rather than write a bunch of code to detect if the app has restarted after a crash, I'd rather it just returned to the main activity each time. I've looked at the clearTaskOnLaunch and launchMode manifest options, but they don't seem to work after crashes. I have an UncaughtExceptionHandler on every thread, but certain crashes (e.g. in native code) just terminate the app without invoking it.
Yeah, I could bundle the client state up and restore it after a crash, but I don't want to serialize potentially sensitive information, and this seems like something the OS would just support since it sounds so basic.
Ok, now waiting for someone to tell me some incredibly obvious solution that I've missed...
Thanks.
The obvious (LOL)Put android:clearTaskOnLaunch="true" in your mainifest:
<activity android:name=".StartActivity"
android:clearTaskOnLaunch="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
savedInstanceState() is used when an Activity is forcefully terminated by the OS and other things i.e.:
when your Activity is in the background and another task needs resources
application crash
orientation change
NOT when user press's the "force close" button in settings
When this happens, onSaveInstanceState(Bundle outstate) will be called and it's up to your app to add any state data you want to save in outstate.
The data is held in memory only while the application is alive, in other words this data is lost when the application closes normally.
The not so obvious stuff (brute force approach [unsubtle])
After crash, check the savedInstanceState object on onCreate method of ALL activity's that might crash, and if it is not null (means it is restarted by android system) then finish the activity (maybe relaunch ?).
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
finish();
}
}
NOTE An activity also gets restarted in configuration changes (e.g. rotation), so if you use auto rotation you need to live with restart or code for it (not too hard).
Advanced stuff
Use UncaughtExceptionHandler() on every android java thread AND native ExceptionHandler() code if you use native (maybe with JNI callback to java).
In Android it is possible to create a Service to do background tasks, etc by creating a subclass of Service. It order to use the Service it must be specified in the manifest for the app:
All services must be represented by elements in the manifest file. Any that are not declared there will not be seen by the system and will never be run.
One of the parameters for a Service in the manifest is the 'enabled' option:
Whether or not the service can be instantiated by the system — "true" if it can be, and "false" if not.
What is the purpose in declaring a Service to be disabled - if I didn't want the Service surely I just wouldn't write it / add it to the manifest in the first place?
The only use I can see for disabling a Service in the manifest, and it seems of limited value, is if it's a Service used only for debugging, and I want it disabled for production. Am I missing something?
The android:enabled attribute set to a boolean value defined in a resource file. The purpose of this attribute is to enable or disable the service on devices running on different Android OS version.
For example, to disable the service on devices running Android 4.3 or lower include menifest attribute android:enabled="#bool/atLeastKitKat".
In addition to including this attribute in the manifest, you need to do the following:
In your bool.xml resources file under res/values/, add this line:
<bool name="atLeastKitKat">false</bool>
In your bool.xml resources file under res/values-v19/, add this line:
<bool name="atLeastKitKat">true</bool>
if I didn't want the Service surely I just wouldn't write it / add it to the manifest in the first place?
In the very specific case of a Service, I agree that it would be rare for you to want to disable it. One possibility would be for a service that plugs into the system (e.g., input method editor, accessibility service) that you only want to enable at runtime (via PackageManager and setComponentEnabledSetting()) if the user make an in-app purchase that unlocks the feature. I am sure that there are other Service scenarios for this, though none are leaping to mind at this early hour of the day (yawn!).
However, I suspect that Service "inherits" its android:enabled setting by virtue of being one of the Android component types, along with activities, providers, and receivers. Other scenarios for android:enabled will be a bit more common with other component types. For example, it is considered good form to have your BOOT_COMPLETED receiver be disabled until you know that you need it. So, for example, if the BOOT_COMPLETED receiver is only used to resume a download interrupted by a reboot, you only need that receiver enabled if you are doing a download. At all other times, you may as well leave it disabled, so you don't waste the user's time during "normal" reboots.
The application that I am working on connects successfully with a device over the USB connection.
Additionally, it now remembers the device from different launches as also pointed out in the following thread storing usb default connections.
However, this leaves the undesired effect of always launching the Activity when the USB device is attached. I have not been able to remember the defaults without launching the application. Launching the application on a connect is not a desired effect. Small code samples below are what the manifest currently looks like in order to get the default USB connections stored in memory for future connections at a quick glance. Can this storage of defaults be done without causing an application launch?
<activity
android:name="com.MainScreen"
android:theme="#style/Theme.Default"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="#xml/device_filter" />
</activity>
I had been thinking that perhaps there was a type of category that I could add to the intent filter that would specify it as a non-launchable intent. Something that was listened for during the activity, but handled by the activity. So far, I haven't found this however.
Update:
Declaring the intent for usb device connection within a service or a receiver in the manifest has also been an attempt at storing connection information. However, only the intent within an activity remembers connection state so that it doesn't need to ask the user a subsequent time when it is re-connected at a later given point in time. It looks as though it is designed only to save state combined with launching an application when it is connected. A bit odd, but no workaround for getting a no-launch combined with remembering the default connection for usb attached device.
You don't have to launch an activity always. You can let a background service handle the intent quietly.
<service android:name=".MyService">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
</service>
Update:
You might have to request some permissions explicitly. Here's what you should do:
When user installs the app and open it first time, ask for USB related permissions.
Set a service to handle USB attach event as mentioned above. When attached, display a persistent notification in status bar as long as a recognized device is attached.
The status bar notification is also useful to notify user that more permissions are required and main UI activity can be accessed.
You should make a small re-design.
Create a Service and run the logic like 'When USB device attached' etc. (probably you have them in that Activity now) within this service.
By the way, you can always make good use of your MainActivity from or apart from this service.
I can confirm by my own experiments that the USB device will not be remembered from within a service. It seems that requestPermission() must come out of an Activity.
To avoid the launch of the activity, I implemented onPostCreate() like this:
#Override // from Activity
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
Intent triggerIntent = getIntent();
if (triggerIntent != null) {
if ( ! triggerIntent.hasCategory(Intent.CATEGORY_LAUNCHER) ) {
Log.e(LOGTAG, "not started by launcher! exit now.");
finish();
}
}
}
I used onPostCreate() instead of pasting the code into onCreate() because when calling finish() from onCreate() it will cause onDestroy() to be called immediately and typically not all members of the Activity class are setup properly yet. It seems to be more safe to me using the onPostCreate() approach.