When using Firebase invites and accessing the dynamic links at the startup of the app on Android, is there a way to know whether the user just installed the app thanks to the invite or whether it was already installed?
Many thanks,
Borja
EDIT: Thanks to Catalin Morosan for the answer
It turns out that you can find this out using method AppInviteReferral.isOpenedFromPlayStore(result.getInvitationIntent()). In the activity that runs when you click on the invitation:
// Create an auto-managed GoogleApiClient with access to App Invites.
mGoogleApiClientInvite = new GoogleApiClient.Builder(this)
.addApi(AppInvite.API)
.enableAutoManage(this, this)
.build();
// Check for App Invite invitations and launch deep-link activity if possible.
// Requires that an Activity is registered in AndroidManifest.xml to handle
// deep-link URLs.
boolean autoLaunchDeepLink = false;
AppInvite.AppInviteApi.getInvitation(mGoogleApiClientInvite, this, autoLaunchDeepLink)
.setResultCallback(
new ResultCallback<AppInviteInvitationResult>() {
#Override
public void onResult(AppInviteInvitationResult result) {
if (result.getStatus().isSuccess()) {
// Extract information from the intent
Intent intent = result.getInvitationIntent();
String invitationId = AppInviteReferral.getInvitationId(intent);
boolean alreadyUser = AppInviteReferral.isOpenedFromPlayStore(result.getInvitationIntent());
if (alreadyUser) {
// Do stuff...
} else {
// Do other stuff...
}
}
}
});
Based on this Google product form post, the Firebase Dynamic Links library will only check for incoming deep links once per app lifetime, meaning you'd need to uninstall and reinstall the app for it to check again. This feeds into the behavior of the getInvitation() method, and it appears you can imply whether the app was previously installed based on the results of this method.
To me, this seems awfully confusing. At Branch.io we do it completely differently: your link data object will always contain an is_first_session boolean, which you can programmatically handle in any way you choose.
Related
I'm working on an app for android phones to be used by multiple users, where they can log in with google or microsoft accounts, to connect the app info to microsoft teams and/or sharepoint if desired.
I'm coding on Android Studio, using MSAL supporting multiple accounts.
Underneath is a method I have to remove accounts from the current PublicClientApplication.MultipleAccountPublicClientApplication. It also returns the result for each removal, if they were removed or not, in a list of booleans.
When testing, all the accounts are removed successfully, but when signing in again and the microsoft sign in intent is opened, the accounts can just be clicked to sign in without password. Signing out seems kind of pointless because of this, since one can just select their user and be logged in again right away. Is it possible to require or force the Microsoft intent to log in with password?
public CompletableFuture<List<Boolean>> signOutAll() {
List<Boolean> removedList = new ArrayList<>();
CompletableFuture<List<Boolean>> future = new CompletableFuture();
for (IAccount account : accountList) {
mPCA.removeAccount(account,
new IMultipleAccountPublicClientApplication.RemoveAccountCallback() {
#Override
public void onRemoved() {
removedList.add(true);
if (accountList.size() == removedList.size()) {
future.complete(removedList);
}
}
#Override
public void onError(#NonNull MsalException exception) {
removedList.add(false);
if (accountList.size() == removedList.size()) {
future.complete(removedList);
}
}
});
}
return future;
}
--
Thank you,
Didrik
This is happening because MSAL automatically refreshes your token after expiration. When user opens your app it checks if that token is already present and valid. So you can remove the token from the Android KeyStore in onStop().
So yes you also need to remove the cache as well to remove the account from the cache, find the account that need to be removed and then call PublicClientApplication.removeAccount()
Set<IAccount> accounts = pca.getAccounts().join();
IAccount accountToBeRemoved = accounts.stream().filter(
x -> x.username().equalsIgnoreCase(
UPN_OF_USER_TO_BE_REMOVED)).findFirst().orElse(null);
pca.removeAccount(accountToBeRemoved).join();
Read more here.
On Android we basically don't have any control on the cookies because they are shared with external Chrome app and because of that it is not accessible. If you want the user to enter the password again then you should do this: AcquireTokenInteractive(scopes).WithPrompt(Prompt.ForceLogin);
I'm new with Xamarin form, my question is how can I check user is logged in for entire app, example every time when users go to a new page, it checks for authent. I tried successfully for every page check for auth but is there any another ways to do it? I did some research on internet and some said that i have to auth on the App.cs on OnStart() but the event is not raise when i go to the next page, it starts only when user open the app.
Here is my code, I'm using Google auth.
On the logged in page (HomePage):
public HomePage(NetworkAuthData networkAuthData)
{
if (string.IsNullOrEmpty(networkAuthData.Id))
{
//Always require user authentication
//Application.Current.MainPage.Navigation.PopAsync();
Application.Current.MainPage = new NavigationPage(new SocialLoginPage(oAuth2Service));
}
else
{
BindingContext = networkAuthData;
InitializeComponent();
}
}
It works but when i move these code to app.cs on OnStart() it just run once when opened the app.
After some back and forth I finally got this to work but I had to use version 0.2.0 because I followed the google guide presented in the Readme.
Anyway, Im struggling with handling what will happen when the oAuth token times out. Then it needs to open the browser again to log in or is there a background process available for this as it automatically redirects back to the app because the server remembers the user so there is no need for a new username/password input?
Im getting a refresh token like this :
if(mAuthService == null){
mAuthService = new AuthorizationService(context);
}
mAuthState.performActionWithFreshTokens(mAuthService, new AuthState.AuthStateAction() {
#Override public void execute(
String accessToken,
String idToken,
AuthorizationException ex) {
if (ex != null) {
return;
}
// Getting the access token...
}
});
Thats working fine but after the user is idle for some time it wont work. How to handle this properly?
Solution for my problem was this:
I changed to using offline_access for the token in the scope. Depending on the site/service you're login into if they accept it or not. For me it was accepted and will keep the user logged in for a long time and removes the need to re-login.
I have created a dynamic link in Firebase console. It has a short url that directs the user to an Activity inside the application.
I have done the same in iOS, using the code:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: #escaping ([Any]?) -> Void) -> Bool
{
if let incomingUrl = userActivity.webpageURL
{
print(incomingUrl) //Here I get the url that the user clicked on
}
}
I'm trying to get the exact output in Android when the user clicks on the dynamic short link.
Currently, I have :
FirebaseDynamicLinks.getInstance()
.getDynamicLink(getIntent())
.addOnSuccessListener(this, new OnSuccessListener<PendingDynamicLinkData>() {
#Override
public void onSuccess(PendingDynamicLinkData pendingDynamicLinkData) {
// Get deep link from result (may be null if no link is found)
Uri deepLink = null;
if (pendingDynamicLinkData != null)
{
deepLink = pendingDynamicLinkData.getLink();
}
// Handle the deep link. For example, open the linked
// content, or apply promotional credit to the user's
// account.
// ...
// ...
}
})
.addOnFailureListener(this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "getDynamicLink:onFailure", e);
}
});
Here, I can get the deep link, I have no clear idea of fetching the short link in Android.
Thank you.
You are misunderstanding the way platforms like Dynamic Links and Branch.io (full disclosure: I'm on the Branch team) work. Your Android implementation is correct — the iOS one is wrong. You'll want to review the Dynamic Links setup guide for iOS to make sure you're all set.
One of the major benefits of using hosted deep links is you don't need the actual URL the user clicked. There are two good reasons for this:
The URL is never available for the deferred deep link use case (when the app isn't already installed).
The URL arrives differently when the app is opened via Universal Links/App Links than via custom URI scheme.
The hosted link platform abstracts those technical details away, so that you can implement your own functionality without worrying about them. If you try to bypass the intended usage, it actually makes things much harder.
I'm using dynamic links for my app.
I've followed the tutorial step-by-step and I'm able to open the app by clicking on the link posted on facebook.
But when I invoke getInvitation, I always have CANCELED as status of AppInviteInvitationResult.
AppInvite.AppInviteApi.getInvitation(mGoogleApiClient, this, false).setResultCallback(
new ResultCallback<AppInviteInvitationResult>() {
#Override
public void onResult(#NonNull AppInviteInvitationResult result) {
if (result.getStatus().isSuccess()) {
// Extract deep link from Intent
Intent intent = result.getInvitationIntent();
String deepLink = AppInviteReferral.getDeepLink(intent);
// [END_EXCLUDE]
} else {
Log.d("StartActivity", "getInvitation: no deep link found.");
}
}
});
Into debug, I can see that result.getStatus() returns CANCELED, but the click on lick open the app correctly.
Where I'm wrong?
EDIT: The link that I'm using is:
https://wft4z.app.goo.gl/?link=https://aqld.it/testlink/112972&al=aqld://test/about?params%3D17363&apn=com.project.mydeeplink
The filter on manifest:
The status is canceled when no intent has been received. I was wondering the same thing and it turned out that my links created in firebase web page were wrong. I wrote some ideas on how to debug the url problem as an answer to another question. If you have the same problem as I did, they should be helpful:
https://stackoverflow.com/a/37615175/4025606
Doesn't directly answer your question but you could eliminate badly formed urls as a root cause by using this page to create firebase dynamic links for both ios and Android: http://fdl-links.appspot.com/
Just double-check if you have added the SHA-1 in the firebase console and the added SHA-1 matches the SHA1 of the generated APK. I was seeing the same issue - result.getStatus() returning CANCELED prior to this, but after adding the SHA-1 on firebase console, it started working fine. :)