I am struggling to configure Firebase Dynamic Links with React Navigation. The dynamic link triggers successfully the App. However, it does not navigate to the correct route (screen.) I am working on Android, and have not, yet, started the more difficult (for me) iOS configuration. I am sure I got one (or more) of the configuration options wrong. I would greatly appreciate your effort and time to help.
The following works and navigates to the correct route:
adb shell am start -W -a android.intent.action.VIEW -d casualjob://InvitedEmployee com.casualjob.
The following starts the app, but does NOT navigate to the correct route:
npx uri-scheme open "https://casualjobsyd.page.link/app?link=https%3A%2F%2Fbuildwebsitepro.com.au%2Fcasualjob%2FInvitedEmployee" --android.
The link preview https://casualjobsyd.page.link/app?d=1 returns the dynamic link tree successfully.
The following command returns the correct json: https://casualjobsyd.page.link/.well-known/assetlinks.json
Firebase Console Configuration:
Dynamic Link Name: https://casualjobsyd.page.link
The Short Dynamic Link: https://casualjobsyd.page.link/app
Deep Link: https://buildwebsitepro.com.au/casualjob
AndroidManifest.xml
...
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true"
>
<intent-filter android:autoVerify="true">
<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="casualjob"/>
</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="http" android:host="casualjobsyd.page.link/app"/>
<data android:scheme="https" android:host="casualjobsyd.page.link/app"/>
</intent-filter>
</activity>
...
Dynamic Links are generated using the following code:
const link = await dynamicLinks().buildLink({
link: "https://buildwebsitepro.com.au/casualjob/",
domainUriPrefix: "https://casualjobsyd.page.link/app",
});
const linkSentToUser = link + "InvitedEmployee";
// result of the above is: "https://casualjobsyd.page.link/app?link=https%3A%2F%2Fbuildwebsitepro.com.au%2Fcasualjob%2FInvitedEmployee"
React Navigation Dynamic Linking Configuration:
const linking = {
prefixes: ["casualjob://", "https://casualjobsyd.page.link/app", ],
config: config,
async getInitialURL() {
const { isAvailable } = utils().playServicesAvailability;
if (isAvailable) {
const initialLink = await dynamicLinks().getInitialLink();
if (initialLink) {
return initialLink.url;
}
}
const url = await Linking.getInitialURL();
return url;
},
subscribe(listener) {
const unsubscribeFirebase = dynamicLinks().onLink(({ url }) => {
listener(url);
});
const linkingSubscription = Linking.addEventListener("url", ({ url }) => {
listener(url);
});
return () => {
unsubscribeFirebase();
linkingSubscription.remove();
};
},
};
I figured it out. The most important change is the need to add every Deep link to the Firebase Console. For the example above, I had to change the following.
The Deep Link defined on Firebase Console was: https://buildwebsitepro.com.au/casualjob
The Deep Link defined on Firebase Console should be: https://buildwebsitepro.com.au/casualjob/InvitedEmployee
Other changes need to be applied as well.
const linking = {
// The 2nd element below should be changed to look like the following.
prefixes: ["casualjob://", "https://casualjobsyd.page.link/app", ],
// ...
}
I learnt that my Androidmanifest.xml was wrong. The following is recommended by the documentation, although it did not seem to be necessary!?
<!-- Note that the android:host must be set to the domain of "your deep link," and NOT the "Dynamic Links" domain -->
<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="buildwebsitepro.com.au" android:pathPrefix="/casualjob/" />
</intent-filter>
<!-- android:autoVerify="true" is required below -->
<!-- Note that the android:host must be set to your "Dynamic Links" domain, and not the domain of "your deep link." -->
<intent-filter android:autoVerify="true">
<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="casualjobsyd.page.link/app"/>
<data android:scheme="http" android:host="casualjobsyd.page.link/app"/>
</intent-filter>
Related
Dynamic link is not opening the installed mobile app. It opens the Play Store instead. I have a button on my website on click of which, I want to open my app. For this, I am opening a dynamic link on click of that button on my website. When I click on the button, in some web browsers, the dynamic link is redirecting to Google Play store app(with my app on the Play Store) and in some browsers, opening Play Store website(with my app on the Play Store) inside the browser. However, when I send that dynamic link in the chat apps like Whatsapp and click on that link from the chat, then its opening my installed application.
I have also uploaded the release and debug SHA-256 keys from my local machine and the Google Play Store on the Firebase Console.
I have gone through several Github issues and stackoverflow questions, but could not find any solution for it.
Mobile
On mobile side, I have dynamic link listeners setup. We are generating the dynamic link on the backend side(code included below).
useEffect(() => {
getInitialDynamicLink();
const unsubscribe = dynamicLinks().onLink(handleDynamicLink);
return () => unsubscribe();
}, []);
const handleDynamicLink = link => {
if (link?.url) {
console.log('shared url is: ', link?.url);
}
};
const getInitialDynamicLink = () => {
dynamicLinks()
.getInitialLink()
.then(link => {
handleDynamicLink(link);
});
};
Backend
On the backend, I am using https://www.npmjs.com/package/firebase-dynamic-links/v/1.0.0 to generate dynamic links.
package.json:
"#react-native-firebase/app": "^16.4.0",
"#react-native-firebase/dynamic-links": "^16.4.0",
"react-native": "0.67.4",
android/build.gradle
ext {
buildToolsVersion = "31.0.0"
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
ndkVersion = "21.4.7075529"
kotlin_version = "1.4.0"
googlePlayServicesAuthVersion = "19.2.0"
}
AndroidManifest.xml:
As mentioned in the doc https://developer.android.com/training/app-links#android-app-links, my app also has the App Link
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:host="appname.page.link" android:scheme="https"/>
</intent-filter>
I am Trying to Authenticate twitter using flutter and firebase. but not able to landing back on my application page after Authorizing on twitter.
Images:
001
001 : launch app login button will redirect to Twitter page(image 002)
002from twitter page when I click on Authorize app it switches to Image 003 "social_demo" which is app label in my androidmenifest.xml.
003then I click back and it redirect to twitter page with error message Image 004.
004finally if I close this or click back, I got debug message in app "TwitterLoginStatus.cancelledByUser".
Steps are:
redirect to twitter from app
Future signInWithTwitter() async {
final twitterLogin = TwitterLogin(
apiKey: "xxxxxxxxxx",
apiSecretKey: "xxxxxxxxx",
redirectURI: 'flutter-twitter-auth://',
);
await twitterLogin.login().then((value) {
debugPrint(value.status.toString());
});
message => TwitterLoginStatus.cancelledByUser
Androidmenifest.xml
<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="flutter-twitter-auth"/>
</intent-filter>
pubspec.yaml
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.4
firebase_auth: ^3.2.0
google_sign_in: ^5.2.1
google_fonts: ^2.1.0
flutter_facebook_auth: ^3.5.6
twitter_login: ^4.0.1
looking for solution... if anyone come across to this.
Twitter: Twitter
I was facing the same problem, (Went through twitter_login: ^4.0.1 documentation) found the culprits.
<data android:scheme="flutter-twitter-auth"/>
Replace the above with,
<data android:scheme="your_app_name"/>
also replace the below snippet
final twitterLogin = TwitterLogin(
apiKey: "xxxxxxxxxx",
apiSecretKey: "xxxxxxxxx",
redirectURI: 'flutter-twitter-auth://',
);
with
final twitterLogin = TwitterLogin(
apiKey: "xxxxxxxxxx",
apiSecretKey: "xxxxxxxxx",
redirectURI: 'your_app_name://',
);
and finally callback URL in twitter developer account should be,
your_app_name://
Recently, My app update got rejected due to Security Vulnerability INTENT REDIRECTION. And as per Google Play console - Security Alerts. Following is the error.
Your app contains an Intent Redirection vulnerability. Please see this bellow mail.
androidx.fragment.app.FragmentActivity.startActivityForResult
after few days we can chat customer support send one mail
I update All dependence in gradel file. after manifest change Exported: "false", we are using camera and gallery and External Documents upload I check the Android web Site update all things up-to-date but I don't found any suspicious code. please help me solve this problem for more details bellow I add my gradle and manifest file
gradle
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 30
defaultConfig {
applicationId "com.example.flowerbazaarseller"
minSdkVersion 21
targetSdkVersion 30
versionCode 30
versionName "1.14"
multiDexEnabled true
vectorDrawables.useSupportLibrary = true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
debuggable false
}
release {
shrinkResources false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
checkReleaseBuilds false
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.recyclerview:recyclerview:1.2.0'
implementation 'androidx.viewpager:viewpager:1.0.0'
//JUnit
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
//swipeRefresh
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
//material theme
implementation 'com.google.android.material:material:1.4.0-alpha02'
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.2'
//Compressor
implementation 'id.zelory:compressor:3.0.1'
//CropImage
//noinspection GradleDynamicVersion
api 'com.theartofdev.edmodo:android-image-cropper:2.8.0'
implementation 'com.github.yalantis:ucrop:2.2.6'
//Glide
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
//OTP retriever
//implementation 'com.google.android.gms:play-services-auth:19.0.0'
//implementation 'com.google.android.gms:play-services-auth-api-phone:17.5.0'
//Dexter Permission handler
implementation 'com.karumi:dexter:6.2.2'
//lottie animation
implementation 'com.airbnb.android:lottie:3.6.1'
//firebase
implementation platform('com.google.firebase:firebase-bom:25.12.0')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-messaging:21.1.0'
implementation 'com.google.firebase:firebase-database:19.7.0'
//multi-dex
implementation 'com.android.support:multidex:1.0.3'
//play-core-library
implementation 'com.google.android.play:core:1.10.0'
//Pdf viewer
implementation 'com.github.barteksc:android-pdf-viewer:2.8.2'
implementation 'com.mindorks.android:prdownloader:0.6.0'
//Easy Image (using take images and fetch gallery)
implementation 'com.github.jkwiecien:EasyImage:3.2.0'
}
Here are the permission I have added manifest file :
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<queries>
<!-- Camera -->
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
<!-- Gallery -->
<intent>
<action android:name="android.intent.action.PICK" />
<data android:mimeType="vnd.android.cursor.dir/image" />
</intent>
<!-- Document -->
<intent>
<action android:name="android.intent.action.PICK" />
<data android:mimeType="vnd.android.cursor.dir/application" />
</intent>
</queries>
Camera:
Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePicture.resolveActivity(getPackageManager()) != null) {
tartActivityForResult(takePicture, CAMERA_REQ_CODE);
}
Gallery:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
intent.setAction(Intent.ACTION_GET_CONTENT);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(Intent.createChooser(intent, "select Picture"), PICK_GALLERY_IMAGE);
}
Document:
Intent chooseFile = new Intent(Intent.ACTION_OPEN_DOCUMENT);
chooseFile.setType("application/*");
chooseFile.addCategory(Intent.CATEGORY_OPENABLE);
chooseFile.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(chooseFile, PICK_FILE_REQUEST_CODE);
above i add all my code snippets can you check once please tell me where is the problem.
Thanks.
We want a user to complete their profile, so an in-app message pops up and if they accept, it redirects them to the edit profile page. It's working, but it redirects to the browser, comes back the app, and then completes the navigation to the profile page. Is there a way to cut out the browser redirect?
I tried setting the intent-filter in the AndroidManifest.xml
<intent-filter>
<data android:host="#string/firebaseDynamicLinkDomain" android:scheme="http"/>
<data android:host="#string/firebaseDynamicLinkDomain" android:scheme="https"/>
</intent-filter>
Here's where we initialize
export const initialize = async () => {
try {
disposeOnLink = dynamicLinks().onLink(handleLink);
} catch (e) {
throw e;
}
};
and then handle the link
const handleLink = (link: string) => {
if (link.url === 'https://mykomae.page.link/edit-profile') {
navigateToEditProfile('profile');
}
};
So it's functional but it looks awful and I'm hoping to avoid this behavior.
This has been a while since you posted but I think you might be missing an intent filter. Just replace example.com with your domain and it should work.
https://firebase.google.com/docs/dynamic-links/android/receive#add-an-intent-filter-for-deep-links
<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:host="example.com"
android:scheme="https"/>
</intent-filter>
Currently I put my html file in assets, and I load it in WebView. Can I load it through chrome custom tab?
Actually there is a way.
in AndroidManifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
define provider paths
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_files"
path="." />
</paths>
And then just extract your local file to external dir
val file = File(activity.externalCacheDir, "hello.html")
val bytes = resources.openRawResource(R.raw.hello).use { it.readBytes() }
FileOutputStream(file).use { it.write(bytes) }
val uri = FileProvider.getUriForFile(activity, "${activity.packageName}.provider", file)
CustomTabsIntent.Builder()
.build()
.also { it.intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }
.launchUrl(activity, uri)
No, it is not possible to open file:// URLs in customtabs.