Firebase Dynamic Links on Android do not survive installation process - firebase

My question is similar to this recent question for iOS.
Firebase Dynamic links work as expected on a device with the app already existing, but I fail to get a referral when I install the app (currently in the beta channel) from the Play Store.
Specifically, AppInviteReferral.hasReferral(getIntent()) returns false when the app is installed from the PlayStore beta channel.
According to the linked answer, Dynamic Links work most of the time, but there may be undocumented edge cases that will cause it to fail. I'll highlight what is specific to my case, so you might help me find what's missing in my setup.
I only just updated my Firebase libraries to 10.2.6 from 10.2.4. There was no change to the Firebase Invites library in the changelog.
If it matters, here's the order in which I include the libraries
compile 'com.google.firebase:firebase-core:10.2.6'
compile 'com.google.firebase:firebase-messaging:10.2.6'
compile 'com.google.firebase:firebase-auth:10.2.6'
compile 'com.google.firebase:firebase-database:10.2.6'
compile 'com.google.firebase:firebase-invites:10.2.6'
My SplashScreenActivity.java serves as both the launcher activity, and the activity that accepts and handles deeplinks. Here's the activity declaration in the AndroidManifest
<activity
android:name=".ui.setup.SplashScreenActivity"
android:label="#string/app_name"
android:theme="#style/SplashTheme">
<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.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https"
android:host="deeplinks.myCompanyDomain.com"
android:pathPrefix="/mobile"/>
</intent-filter>
</activity>
SplashScreenActivity.java does not setContentView(int id). It just uses a theme to display the splash screen while the rest of the app's resources "load". I don't know if this matters, but I'm putting it out there.
Before anything starts on the app, I check to make sure the app has the needed permissions. A continueIntoApp() method (I couldn't think of a better name) takes the user into the app when it finds it has the needed permissions, or after the user grants the app all four permissions it needs.
continueIntoApp() is where all the code found on the Firebase Dynamic Links Docs is implemented. I first build and connect a GoogleApiClient.
GoogleApiClient googleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, new GoogleApiClient.OnConnectionFailedListener() {
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
LogUtils.e("Deeplink connection failed");
LogUtils.e(connectionResult.getErrorMessage());
LogUtils.e(String.valueOf(connectionResult.getErrorCode()));
}
})
.addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
#Override
public void onConnected(#Nullable Bundle bundle) {
LogUtils.d("Connected!");
}
#Override
public void onConnectionSuspended(int i) {
LogUtils.e("Connection suspended!");
}
})
.addApi(AppInvite.API)
.build();
googleApiClient.connect();
Just as an aside, the Dynamic Links docs assume the developer already knows how to setup a GoogleApiClient. I didn't. After a few frustrating days, I accidentally found the #connect() method that actually got the GoogleApiClient doing what it was supposed to do.
After this, I check if the AppInviteReferral has a referral.
//boolean autoLaunchDeepLink = true;
if(AppInviteReferral.hasReferral(getIntent())){
LogUtils.d("Referral found!");
AppInvite.AppInviteApi.getInvitation(googleApiClient, SplashScreenActivity.this, true)
.setResultCallback(new ResultCallback<AppInviteInvitationResult>() {
#Override
public void onResult(#NonNull AppInviteInvitationResult appInviteInvitationResult) {
LogUtils.d("Processing appInviteInvitationResult...");
if(appInviteInvitationResult.getStatus().isSuccess()){
Intent intent = appInviteInvitationResult.getInvitationIntent();
String deepLink = AppInviteReferral.getDeepLink(intent);
LogUtils.d("Deeplink is " + deepLink);
AppConfig appConfig = new AppConfig(SplashScreenActivity.this);
appConfig.put(ModelKeys.TEMP_JOIN_BRANCH_DEEPLINK, deepLink);
startActivity(new Intent(SplashScreenActivity.this, MainActivity.class));
//parseDeeplink(deepLink);
}else {
LogUtils.d("No deeplink found!");
startActivity(new Intent(SplashScreenActivity.this, MainActivity.class));
}
}
});
}else {
LogUtils.d("No referral found!");
startActivity(new Intent(SplashScreenActivity.this, MainActivity.class));
}
You will notice I have commented out autoLaunchDeepLink and, by default, pass true to AppInvite.AppInviteApi.getInvitation(). I'm still not sure when I should set this value to true or false. I also don't know how, after a fresh installation from a Dynamic Link (with autoLaunchDeepLink as false), Firebase knows how to "start the dynamic link".
That's as far as the Dynamic Links implementation goes. My problem is as stated above: when I have the app already installed, AppInviteReferral.hasReferral(getIntent()) returns true , and the code runs as normal. When the user follows the Dynamic Link to the PlayStore and downloads the beta release, AppInviteReferral.hasReferral(getIntent()) returns false, and the deeplink is not followed.
Why is this happening? What am I missing?

I don't think you're missing anything - it does seem like the Play Store doesn't send INSTALL_REFERRER broadcasts for the Beta channel installs, and its that referrer which is used as the mechanism for passing the deeplink post-install.
It should work OK if you're using a product app, but it is a little curious that the beta installs don't support that.

Had the same issue. Our problem was that we had two intent-filters almost similar in the AndroidManifest.xml, which caused the Google Play to lose the intent we wanted. Instead of showing "Continue" button it redirected us to uninstall/open page on the play.
Suggesting to work with
https://firebase.google.com/docs/dynamic-links/android/receive

Related

Cancel Navigating event in Xamarin Forms webview

I'm trying to open tel links in my xamarin forms application webview using the Navigating event:
public async void WebViewNavigating(object sender, WebNavigatedEventArgs args)
{
if (args.Url.StartsWith("tel:"))
{
await Xamarin.Essentials.Launcher.OpenAsync(args.Url);
}
}
But I have two issues, first on IOS it gives an error 500 and next in Android as the navigation is not cancelled, I have a not found error and going back to the app after the call.
I read that args.cancel=true is the way to go but the variable doesn't seem to exist no more.
At first, you can use the Xamarin.Essentials: Phone Dialer to open the telephone number. Furthermore you need to test on the physical device for the ios, because the ios simulator can't call number and will throw an exception.
Open the AndroidManifest.xml file under the Properties folder and add the following inside of the manifest node:
<queries>
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel"/>
</intent>
</queries>
Use the following code:
public void WebViewNavigating(object sender, WebNavigatingEventArgs args)
{
if (args.Url.StartsWith("tel:"))
{
string number = args.Url.Split(':')[1];
Xamarin.Essentials.PhoneDialer.Open(number);
args.Cancel = true;
}
}
In adition, you can also use the custom renderer to do that, for more information, you can check my answer in this case.

Huawei push kit message not received from clevertap push console

Here we integrate clevertap sdk as below, somehow HMS push kit msg is not received:
<service
android:name="com.panda.demo.HmsPushService"
android:exported="false">
<intent-filter>
<action android:name="com.huawei.push.action.MESSAGING_EVENT" />
</intent-filter>
</service>
implementation 'com.clevertap.android:clevertap-android-sdk:3.8.2'
public class HmsPushService extends HmsMessageService {
private static final String TAG = "pushdemo";
#Override
public void onNewToken(String token) {
super.onNewToken(token);
Log.i(TAG, "receive token:" + token);
}
You have to integrate CleverTap SDK for Huawei as mentioned here.
For latest Push Kit versions, please refer to Huawei Push Kit documentation here:
Library version is wrong, 'com.clevertap.android:clevertap-android-sdk:4.0.0' , check here
Remove "service" from manifest, you will receive msg
a. If you choose to have the "service" , the HmsPushService Class can not be empty like this. It should have at least more code like the sample in
github: onMessageReceived()
which call
CleverTapAPI.createNotification(getApplicationContext(),extras)
b. add more code in this class to handle push, data, in-app
If above steps do not work, please provide further details per your case.

Can't disable Crashlytics in a Firebase app (anymore)

After upgrading to com.crashlytics.sdk.android:crashlytics:2.7.1#aar (from 2.6.8), I can't disable Crashlytics anymore in my Firebase app.
Looks like there's some code in Crashlytics library itself that initializes Fabric with Crashlytics kit enabled whenever it detects that it's running inside a Firebase application. Indeed initializing with Crashlytics enabled and with ext.enableCrashlytics = false throws an UnmetDependencyException and crashes the app at startup (in fact, before my code in Application.onCreate runs).
Does anyone know a workaround for that? Sticking with 2.6.8 works for now.
This is what I have in my code that used to work until an upgrade:
app/build.gradle:
ext.enableCrashlytics = false
Application.java (onCreate, full method body as requested):
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
// First Fabric invocation
Fabric.with(this, new Crashlytics.Builder().core(
new CrashlyticsCore.Builder().disabled(true).build()).build());
RxJavaPlugins.setErrorHandler(e -> LOGGER.error("Undeliverable RxJava error", e));
// First Firebase invocation
FirebaseDatabase db = FirebaseDatabase.getInstance();
if (BuildConfig.DEBUG) {
db.setLogLevel(com.google.firebase.database.Logger.Level.DEBUG);
}
db.setPersistenceEnabled(true);
according to mike answer, im add my code:
Gradle:
buildTypes {
release {
manifestPlaceholders = [crashlyticsEnabled: true]
}
debug {
manifestPlaceholders = [crashlyticsEnabled: false]
}
}
Manifest.xml:
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="${crashlyticsEnabled}" />
Mike from Fabric here. Use:
<meta-data android:name="firebase_crashlytics_collection_enabled" android:value="false" />
if you want to disable Crashlytics while using Firebase.
Along with mikes above answer,
If you are setting firebase crash properties somewhere in your code, make sure that you don't set them for debug code, otherwise you might notice strange behaviour for the app.
if (!BuildConfig.DEBUG) {
Crashlytics.setUserIdentifier(DataStore.storeId)
}

Solution for BLE scan's SCAN_FAILED_APPLICATION_REGISTRATION_FAILED?

My Android app scans BLE devices, and from a certain point it start to fails with error code 2 (ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED). I'm using Nexus 9, 5.0.1 Lollipop.
This problem continued even after I relaunched the app, and when I restarted the Bluetooth service from Settings, I could finally get rid of the problem. But this problem is recurring, and I think I'm coding in a wrong way; BLE related APIs are new and there is few information.
Does anyone know a general solution for this error, preferably not requiring restart of the Bluetooth service? Even though this error code is documented in Android API reference, I don't know how to handle it properly.
When you got the error
SCAN_FAILED_APPLICATION_REGISTRATION_FAILED
You should disable the BluetoothAdapter
BluetoothAdapter.getDefaultAdapter().disable();
Disabling BluetoothAdapter, the event STATE_TURNING_OFF is fired. Once this event is fired, try to reconnect to the BluetoothAdapter:
case BluetoothAdapter.STATE_OFF:
Log.d(TAG, "bluetooth adapter turned off");
handler.postDelayed(new Runnable() {
#Override
public void run() {
Log.d(TAG, "bluetooth adapter try to enable");
BluetoothAdapter.getDefaultAdapter().enable();
}}, 500);
break;
It turns out that Bluetooth LE requires the following Android application permissions in AndroidManifest.xml:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--BLE scanning is commonly used to determine a user's location with Bluetooth LE beacons. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- if your app targets API level 21 or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
<!--app is available to BLE-capable devices only. -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
Besides on main activity:
// onResume()
if (ContextCompat.checkSelfPermission(this.getApplicationContext(),
android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
} else {
ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_LOCATION_ENABLE_CODE);
}
You should perform operations only success initialization of BT adapter.
To be sure that it is ready create intent filter:
val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
and broadcast receiver(you will perform action only if adapter is ready):
val broadcastReceiver = object: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val action = intent?.action
if (action != null && action == BluetoothAdapter.ACTION_STATE_CHANGED) {
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
when (state) {
BluetoothAdapter.STATE_ON -> {
if (bluetoothAdapter.isEnabled) {
//perform your task here
}
}
BluetoothAdapter.STATE_OFF -> {}
}
}
}
}
then register receiver:
registerReceiver(broadcastReceiver, filter)
and relaunch adapter(this part can be replaces with check):
bluetoothAdapter.disable()
bluetoothAdapter.enable()
DONE!
I had this happen to me today. While manually disabling and then enabling BT in the Android Settings did not fix this, I was able to get it working after only disabling it manually and then have the app that is affected by the issue enable BT.
The app then pops up an Android System message "An app is requesting permission to turn on BT" (I have a German UI, so it may be worded differently), and when I then press allow, the app finally has proper access to BT and this error no longer shows.
Sounds like a bug or something.

java.lang.RuntimeException: Cannot create map: Failed to read configuration file:

I am implementing HERE maps for Android applicaiton, I keep getting a crash in the HERE map SDK with the following error
Process: my.app.package, PID: 30682
java.lang.RuntimeException: Cannot create map: Failed to read configuration file: /data/user/0/my.app.package/files/.here-maps/someid/mwconfig_client
at com.nokia.maps.MapImpl.<init>(MapImpl.java:451)
at com.here.android.mpa.mapping.Map.<init>(Map.java:710)
at com.nokia.maps.bw.m(MapFragmentImpl.java:546)
at com.nokia.maps.bw.a(MapFragmentImpl.java:583)
at com.nokia.maps.bw.a(MapFragmentImpl.java:85)
at com.nokia.maps.bw$1.onEngineInitializationCompleted(MapFragmentImpl.java:135)
at com.nokia.maps.MapsEngine$10.run(MapsEngine.java:2471)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6753)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:482)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Its not reproducible always but its happens 50% of the time.
I can see this issue still open in HERE maps site, https://github.com/heremaps/here-android-sdk-examples/issues/134
I seek help from the HERE maps team, and any solution to this problem will be helpful.
Thanks in advance.
It's probbaly the case that there is another app on your device using the SDK and the MapServices from the two apps are conflicting with each other. You could try setting the isolated disck cache for your app as desribed in the documentation (https://developer.here.com/documentation/android-premium/dev_guide/topics/map-service.html) helps
AndroidManifest.xml
<service
android:name="com.here.android.mpa.service.MapService"
android:label="{YOUR_LABEL_NAME}"
android:exported="false">
<intent-filter>
<action android:name="{YOUR_INTENT_NAME}">
</action>
</intent-filter>
</service>
MapActivity.java
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Search for the map fragment to finish setup by calling init().
mapFragment = (MapFragment) getFragmentManager().findFragmentById(R.id.mapfragment);
boolean success = com.here.android.mpa.common.MapSettings.setIsolatedDiskCacheRootPath(
getApplicationContext().getExternalFilesDir(null) + File.separator + ".here-maps",
"{YOUR_INTENT_NAME}");
if (!success) {
// Setting the isolated disk cache was not successful, please check if the path is valid and
// ensure that it does not match the default location
// (getExternalStorageDirectory()/.here-maps).
// Also, ensure the provided intent name does not match the default intent name.
} else {
mapFragment.init(new OnEngineInitListener() {
...
}
In my case this error is happening just in android 11 version. After some research I have found that changing Files Directory from getExternalFilesDir to getFilesDir works fine for me. In that case the success variable should be:
boolean success = com.here.android.mpa.common.MapSettings.setIsolatedDiskCacheRootPath(
m_fragment.getContext().getFilesDir().getPath() + File.separator + ".here-maps",
"MAP_INTENT KEY that Should match with key in manifest file");

Resources