Flutter App Cloned with Nfc App Logo when launching via Ndef intent - android

I want to launch my Flutter Android App automatically, after an Ndef Tag has been discovered. I'm using an Intent filter to achieve this.
All works fine, apart that when launched via the Ndef Tag, the App has an Nfc App Icon.
Also, if the app was already opened manually before, it will have the App open twice:
a) The manually opened one with the correct logo
b) The Ndef Tag launched one
Screenshot with two instances of the app:
To exclude any side effects, I did a new Flutter project from scratch and only did the following changes in ...\android\app\src\main\AndroidManifest.xml:
Added this right after the <manifest> tag:
<uses-permission android:name="android.permission.NFC"/>
And after the existing
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
I added this filter to launch the app:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="https" android:host="developer.android.com" android:pathPrefix="/test" />
</intent-filter>
The Nfc Tag only has one record of type Uri, with the content https://developer.android.com/test
How can I avoid the app clone, and just always open the same App instance with the correct App logo?

To rely on the basic intent nfc functionality you need to add android:launchMode="singleTop" option to the Activity with the NFC intent filter in the Manifest.
From the docs
the "standard" and "singleTop" modes differ from each other in just one respect: Every time there's a new intent for a "standard" activity, a new instance of the class is created to respond to that intent. Each instance handles a single intent. Similarly, a new instance of a "singleTop" activity may also be created to handle a new intent. However, if the target task already has an existing instance of the activity at the top of its stack, that instance will receive the new intent (in an onNewIntent() call); a new instance is not created
As standard is the default launchmode then a new instance of the Activity is created when the NFC is scanned every time, hence multiple instances.
With singletop it will re-use one that is at the top of the stack BUT you will have to handle the processing the data in the onNewIntent method as well as where you are processing it now.
Using the method does have the downside that your App with be paused and resumed to receive the NFC data, so your App has to pause and resume correctly. It is generally better to use one of the Foreground NFC methods like enableReaderMode instead of the basic intent methods, for Flutter there are various flutter packages to do this.

Related

SingleTask activity, but opened through two different applications

This image was quite helpful for understanding the
functionality offered by the launhmode singleTask, taken from here
however, I understood this in case of the same application, I am having issues understanding
what if both tasks belong to two different Applications
Confusing Scenario(fictional),
I was going through an app and the app offered an action to send
emails, I selected 'send email' option.
My phone's default 'email app' will be picked and its activity (which is
declared as singletask) will be opened.
While I was typing my email content, I switched to some chat app and
the app gets crashed and offered me an option to report an issue
over email to the developer, Now when I will select 'Report' , my email
app(which is the same default email app ) will be opened.
Now as the Email app's root activity is singletask, will my content
which I wrote will be visible to me?
The main thing is this time, the tasks/stacks belong to two different apps.
Even though you are using 2 different applications, it will work in the expected way:
if your singleTask activity already exists, that copy will be used, with the method onNewIntent() being called
if it does not exist, it will be launched as per normal
More technically, reproducing the definition from your link:
The system creates a new task and instantiates the activity at the
root of the new task. However, if an instance of the activity already
exists in a separate task, the system routes the intent to the
existing instance through a call to its onNewIntent() method, rather
than creating a new instance. Only one instance of the activity can
exist at a time.
This can easily be verified by making an activity a target for sharing text and singleTask in the manifest:
<activity
android:name=".MainActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
</activity>
Now add some logging to the onCreate() and onNewIntent() methods and do some scenario testing.
Something I found particularly useful when testing the various launchmodes is the following ADB command:
adb dumpsys activity activities
This outputs a lot of text (it may help to reboot the phone before doing this - adb reboot) showing details of the activity task stacks. This can be used to show you that your singleTask activity "rehomes" itself as it gets launched via different applications.
As for the question about the emails, I think that will depend on which email client you are using, but I would hope that they handle the onNewIntent() method correctly, and save the current draft before displaying your new email.

POC - Start Service on boot without an Activity - Android 4+

This question may seem a trivial but I've been struggling with it.
I'm trying to start a service on boot and everything works fine if I start it at least once from the mainActivity (launch activity), something like:
AndroidManifest.xml
...
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
...
<activity
android:name="com.example.mainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.example.bootReceiver" android:enabled="true" android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<service android:name="com.example.someService" android:enabled="true" android:exported="false"/>
...
bootReceiver.java
...
startService(new Intent(getApplicationContext(), com.example.someService.class));
...
mainActivity.java
...
startService(new Intent(getApplicationContext(), com.example.someService.class));
...
With the above code I'm able to run the service after every boot without any problems.
As a POC, I'm trying to start a service without any activity, or at least without the mainActivity, just by declaring the service on the AndroidManifest.xml or by creating new activity (invisible?) that is launched at the same time as the default launcher activity.
As far a I know, this isn't possible on android 3+ (4+?) due to security reasons.
Is there any way to achieve this ?
Can I start 2 activities from the AndroiManifest.xml when the user launches the app?
I'm sorry if my question isn't 100% clear, I've tried to explain it the best I could, if you cannot understand it, please leave a comment bellow. Tks.
As far a I know, this isn't possible on android 3+ (4+?) due to security reasons.
Android 3.1, actually, to help prevent drive-by malware.
Is there any way to achieve this ?
Something has to use an explicit Intent to invoke some form of IPC on one of your components, to move the app out of the so-called "stopped state" that is preventing you from receiving the broadcast. So, something needs to either:
start one of your activities via an explicit Intent, or
start one of your services via an explicit Intent, or
send a broadcast to one of your receivers via an explicit Intent
(I don't know if trying to connect to a ContentProvider will work, though arguably it should)
The key is the explicit Intent. That's why invoking an activity from a home screen launcher works, because the Intent used to start your activity will be an explicit one.
However, in the absence of such an activity, you would need to find something else that would use an explicit Intent to invoke one of your components. Certain specialized services (e.g., an input method) probably get invoked with an explicit Intent if and when the user activates that app's capability via the system Settings app. If you're a plugin for some other app, that other app might use an explicit Intent to work with one of your components. You can ask the user to install the Android SDK, learn how to use the command line, and invoke an adb shell am command to start one of your components. And that's about all I can think of off the top of my head. None are exactly general purpose solutions.
or by creating new activity (invisible?) that is launched at the same time as the default launcher activity
I have no idea what you think that would achieve. If the user starts up your launcher activity, you're already out of the stopped state and will receive broadcasts as normal.

Activity exported=false listed in activity chooser

I have two similar applications (one free, one paid).
An activity is defined with exported="false"
<activity
android:name=".MyActivity"
android:exported="false"
android:noHistory="true" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/vnd.mine" />
</intent-filter>
</activity>
When I call startActivity with the appropriate implicit intent from the free app, the activity picker appears.
I don't understand why the activity from the paid app appears, since it is exported="false"
I suppose I can add an intent filter based on the URL, but my question is: why does the activity from the other app appear when the doc reads
Whether or not the activity can be launched by components of other applications
I don't understand why the activity from the paid app appears, since it is exported="false"
Because you have a matching <intent-filter>. Since you do not need the <intent-filter> for a non-exported activity, simply delete it and use an explicit Intent when starting this activity.
why does the activity from the other app appear when the doc reads...
I had the same question and was told that this was expected behavior and the bug is in our app for having a useless <intent-filter>. Quoting Dianne Hackborn:
I would generally consider this a bug in the app -- if you have an activity that you aren't allowing other apps to launch, why the heck are you publishing an intent filter that they will match to try to launch? The security of the activity (whether it is not exported or requires a permission) is not part of intent matching. ...this scenario (publishing an activity that matches intents other applications will use but then restricting it to not be launchable by other applications) is not useful if not outright broken.

Broadcast receiver not receiving intent

I have two apps that I have complete control over. Both are signed with the same cert and both use the exact same intent filter. One sends the broadcast from a fragment, the other is suppose to receive it and do something. This however is not working:
Strings.FILTER_INIT_REGISTER = "com.app.FILTER_INIT_REGISTER"
Intent intent = new Intent(Strings.FILTER_INIT_REGISTER);
getActivity().sendBroadcast(intent);
I have registered the receiver in the Manifest app tag for the app containing the ReportingReceiver class:
<receiver
android:name=".receivers.ReportingReceiver"
android:exported="true"
>
<intent-filter>
<action android:name="com.app.FILTER_INIT_REGISTER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
Curious why the ReportingReceiver class is not getting the intent call?
If your application only has a service and receivers then this won't work in Android 3.1 and later. The reason is that the system will not send broadcast Intents to application that are in the STOPPED STATE. An application is in the STOPPED STATE when it is first installed. It is removed from the STOPPED STATE when the user manually starts the application for the first time. It is returned to the STOPPED STATE if the user forces the application to stop using the application manager tool.
Since your application has no Activities, there is no way for the user to "start" it. Therefore it will never come out of the stopped state.
See http://developer.android.com/about/versions/android-3.1.html#launchcontrols
As Android Addict says in his comment to David Wasser's answer ... there is a way around this behaviour.
Just add the following flag to the calling Intent. This will ensure that you also reach broadcast receivers from "stopped" applications.
http://developer.android.com/reference/android/content/Intent.html#FLAG_INCLUDE_STOPPED_PACKAGES
You can read more about this Android 3.1 change here
http://developer.android.com/about/versions/android-3.1.html#launchcontrols
and here
http://code.google.com/p/android/issues/detail?id=18225

Android Can Applicaton Detect How it is being Launched?

Is there a way for Android application to detect how it is being launched? whether by a BOOT or by user launching the application from the application list?
When my application is launched on boot I don't want any activity to show at all. When the user specifically launches it from the application list, then and only then would I want the main activity to show. If I could detect whether the launch was a user launch or system boot launch I might be able to control this.
What you're looking for is the Intent that started the application. In the manifest file of your Android project you can specify which Intents will launch which Activities. The documentation page for Intent actually has a really thorough explanation of how to use this feature, with example code and everything:
http://developer.android.com/reference/android/content/Intent.html
The Intent that will be called when a user selects your application from a launcher will look like:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
If you want a different Activity to launch when the phone boots (there's an intent filter value "android.intent.action.BOOT_COMPLETED"), you just add a different IntentFilter to the Activity's tag in the manifest.

Categories

Resources