Flutter Deep linking is not working in cold state - android

i was doing deep linking for my flutter app.
The dependency i am using is AppLinks.
Everything is working perfectly fine for iOS but for android, it is only working when the app is running in the background.
If we remove the app from the background, it's not getting opened or say not working.
I am seeking a solution for that.
This is my flutter code below :-
`Future<void> initDeepLinks() async {
_appLinks = AppLinks();
final appLink = await _appLinks.getInitialAppLink();
if (appLink != null) {
refresh();
openAppLink(appLink);
print('getInitialAppLink: $appLink');
}
_linkSubscription = _appLinks.uriLinkStream.listen((uri) {
print('onAppLink: $uri');
update();
openAppLink(uri);
});
}
void openAppLink(Uri uri) {
if (uri.path.contains('activeOrder')) {
onBottomNavItemPressed(3);
Get.find<OrdersController>().tabController?.index = 0;
}
if (uri.path.contains('request')) {
onBottomNavItemPressed(3);
Get.find<OrdersController>().tabController?.index = 3;
}
if (uri.path.contains('orderReview')) {
Get.to(() => OrderReviewView());
}
if (uri.path.contains('payoutHistory')) {
Get.to(() => TransactionHistory());
}
if (uri.path.contains('payoutDetails')) {
var transactionDetailsController =
Get.put(TransactionHistoryController());
// print(uri.queryParameters['transaction_id']);
transactionDetailsController.getPaymentDataDetails(
trxId: uri.queryParameters['transaction_id'] ?? "");
Get.to(() => PayoutHistoryDetailPage());
}
}
`
This is the code for the same and working fine for iOS, but it's not working for Android.

have you added the deeplink configuration to the AndroidManifest.xml ?
this is the way i'm using deeplink in my app and it works fine.
first of all I use uni_links package:
and for android in manifest i add the code below in activity tag:
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:theme="#style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
"....the codes ...."
<!-- 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" />
<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
<data
android:scheme="exampleapp"
android:host="example"/>
</intent-filter>
</activity>
as you see the link that opens your app should be like this, for example you are increasing your wallet and after payment the link is :
exampleapp://example/payment?success
see the code below how i use to back to the app:
bool _initialURILinkHandledIncreaseBalance = false;
class IncreaseBalanceModal extends StatefulWidget {
const IncreaseBalanceModal({Key? key}) : super(key: key);
#override
State<IncreaseBalanceModal> createState() => _IncreaseBalanceModalState();
}
class _IncreaseBalanceModalState extends State<IncreaseBalanceModal> {
StreamSubscription? _streamSubscription;
TextEditingController amountTextController =
MaskedTextController(mask: '0', length: 50);
Future<void> _initURIHandler() async {
if (!_initialURILinkHandledIncreaseBalance) {
_initialURILinkHandledIncreaseBalance = true;
try {
final initialURI = await getInitialUri();
// Use the initialURI and warn the user if it is not correct,
// but keep in mind it could be `null`.
if (initialURI != null) {
debugPrint("Initial URI received $initialURI");
if (!mounted) {
return;
}
} else {
debugPrint("Null Initial URI received");
}
} on PlatformException {
// Platform messages may fail, so we use a try/catch PlatformException.
// Handle exception by warning the user their action did not succeed
debugPrint("Failed to receive initial uri");
} on FormatException catch (err) {
if (!mounted) {
return;
}
debugPrint('Malformed Initial URI received $err');
}
}
}
/// Handle incoming links - the ones that the app will receive from the OS
/// while already started.
void _incomingLinkHandler() {
if (!kIsWeb) {
// It will handle app links while the app is already started - be it in
// the foreground or in the background.
_streamSubscription = uriLinkStream.listen((Uri? uri) {
if (!mounted) {
return;
}
debugPrint('Received URI hello we are home: $uri');
var query = uri!.query;
if(query == 'failed'){
locator<NavigationService>().goBack();
snackBar(S.of(context).transactionWasUnsuccessfulText, context,color: brandMainColor);
}
else if(query == 'success'){
onTapBackToApp();
}
}, onError: (Object err) {
if (!mounted) {
return;
}
debugPrint('Error occurred: $err');
});
}
}
#override
void initState() {
super.initState();
_initURIHandler();
_incomingLinkHandler();
}
#override
void dispose() {
amountTextController.dispose();
if (!kIsWeb) _streamSubscription!.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
var width = MediaQuery.of(context).size.width;
return Consumer<UserProvider>(
builder: (context,userProvider, child) {
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CustomAppBar(
title: S.of(context).increaseBalanceText,
hasBackButton: false,
color: primaryDark,
),
Padding(
padding: EdgeInsets.symmetric(horizontal: width * 0.04),
child: Column(
children: [
SizedBox(
height: width * 0.04,
),
TextFormFieldItem(
labelText: amountLabelText,
textController: amountTextController,
minHeight: width * 0.1333,
onChanged: (s) {
setState(() {});
}
),
],
),
),
Padding(
padding: EdgeInsets.all(width * 0.04),
child: RectAngleButton(
state: ViewState.ready,
nameOfButton: S.of(context).submitText,
color: amountTextController.text.trim().isNotEmpty
? brandMainColor
: secondaryDark,
height: width * 0.1493,
width: width,
onTap: amountTextController.text.trim().isNotEmpty
? () async {
var url = await UserService.createPaymentByUser(
Provider.of(context, listen: false),
Provider.of(context, listen: false),
URLs.connectToGateMutation(), {
"amount" : int.parse(amountTextController.text)
}
);
if (url.isNotEmpty) {
_launchURLBrowser(url);
} else {
snackBar(
S
.of(context)
.somethingWentWrongPleaseTryAgainText,
context);
}
}
: null,
),
),
],
)
);
});
}
void onTapBackToApp() async {
await UserService.getUser(Provider.of(context,listen: false), Provider.of(context,listen: false),Provider.of(context, listen: false),
Provider.of(context,listen: false), URLs.meQuery(),false);
locator<NavigationService>().goBack();
snackBar(S.of(context).transactionWasSuccessfulText, context,color: greenColor);
}
_launchURLBrowser(String url) async {
if (await canLaunch(url)) {
await launch(url, forceSafariVC: !kIsWeb
? defaultTargetPlatform == TargetPlatform.iOS
? false
: null
: null,
webOnlyWindowName: kIsWeb ? '_self' : '_blank');
} else {
throw 'Could not launch $url';
}
}
}
even if you close the app, the deeplink will open it again, and you can manage the navigation by the query of your link like what:
from this link exampleapp://example/payment?success you can understand that it is coming to the app because of payment:
var type = uri.pathSegments.first;//payment
and the type tells you to navigate to payment page from the beginning.
Edit: these explanation that I've said before is completely true, and when I checked your Manifest code I saw that you have multiple configs for your deeplink.
<!-- Deep linking -->
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<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="http" android:host="host.com" android:pathPrefix="/orderReview"/>
<data android:scheme="https" />
</intent-filter>
<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="http" android:host="host.com" android:pathPrefix="/activeOrder"/>
<data android:scheme="https" />
</intent-filter>
<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="http" android:host="host.com" android:pathPrefix="/request"/>
<data android:scheme="https" />
</intent-filter>
<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="http" android:host="host.com" android:pathPrefix="/payoutHistory"/>
<data android:scheme="https" />
</intent-filter>
<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="http" android:host="host.com" android:pathPrefix="/payoutDetails"/>
<data android:scheme="https" />
</intent-filter>
<!-- <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="http" android:host="host.com" android:pathPrefix="/request"/>
</intent-filter>
<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="http" android:host="host.com" android:pathPrefix="/activeOrder"/>
</intent-filter>
<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="http" android:host="host.com" android:pathPrefix="/payoutHistory"/>
</intent-filter>
<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="http" android:host="host.com" android:pathPrefix="/payoutDetails"/>
</intent-filter>
<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="host.com" android:pathPrefix="/orderReview"/>
</intent-filter>
<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="host.com" android:pathPrefix="/request"/>
</intent-filter>
<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="host.com" android:pathPrefix="/activeOrder"/>
</intent-filter>
<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="host.com" android:pathPrefix="/payoutHistory"/>
</intent-filter>
<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="host.com" android:pathPrefix="/payoutDetails"/>
</intent-filter> -->
it is not recommended to do add all the configs with all the pathPrefix in your app, if you have just one pathPrefix it will be ok to do that but please delete those lines that you've added before and please add these lines below instead:
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Accepts URIs that begin with YOUR_SCHEME://YOUR_HOST -->
<data
android:scheme="https"
android:host="host.app"/>
</intent-filter>
you should use main config in your manifest and check your pathPrefix in your Flutter code.
happy coding...

Related

Firebase Magic Link in Jetpack Compose

I'm working on Firebase's Passwordless Authentication using Android. The Magic Link is received via email, but clicking it only opens the specified URL in Chrome, instead of my app. Here's how things are setup on the client, let me know if relevant code is missing. I've also posted .well-known/assetlinks.json at the root of my site hoping it would help, to no avail.
<!-- AndroidManifest.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:host="es0329.com" android:scheme="https" />
</intent-filter>
// Screens.kt
sealed class Screens {
object Feed : Screens(
route = "feed",
title = R.string.feed,
icon = Icons.Rounded.TravelExplore
) {
val deepLinks: List<NavDeepLink> = listOf(
navDeepLink {
uriPattern = "$deepLinkBaseUrl$route"
action = Intent.ACTION_VIEW
})
}
}
companion object {
const val deepLinkBaseUrl = "https://es0329.com/"
}
// AuthStore.kt
actionCodeSettings {
url = "${Screens.deepLinkBaseUrl}feed"
handleCodeInApp = true
setAndroidPackageName(
BuildConfig.APPLICATION_ID,
INSTALL_IF_NOT_AVAILABLE,
BuildConfig.VERSION_NAME
)
}
// NavGraph.kt
composable(
route = Feed.route,
deepLinks = Feed.deepLinks
) {
BreweryList()
}
The manifest's opening intent-filter tag was missing the autoVerify attribute.
<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="es0329.com" android:scheme="https" />
</intent-filter>

How to set up firebase Authentication (with ```twitter_login: ^4.0.1``` and ```flutter_facebook_auth: ^4.0.1``` respectively) in Flutter?

Firebase Authentication (with twitter_login: ^4.0.1 and flutter_facebook_auth: ^4.0.1 respectively) in Flutter. I am getting errors in both the Authentication processes. I had also setup in Facebook Login and Twitter Login in developer account. Also went through many articles online but it seems none were working. After the recent updates.
error Message Received:
E/com.facebook.GraphResponse(13052): {HttpStatus: 400, errorCode: 100, subErrorCode: 33, errorType: GraphMethodException, errorMessage: Unsupported get request. Object with ID 'XXXXXXXXXXXX' does not exist, cannot be loaded due to missing permissions, or does not support this operation. Please read the Graph API documentation at https://developers.facebook.com/docs/graph-api}
The above message was from Facebook the below from Twitter
E/flutter (13052): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: PlatformException(400, Failed to generate request token., Please check your APIKey or APISecret., null)
AndroidManifest.xmlfile.
<meta-data android:name="com.facebook.sdk.ApplicationId"
android:value="#string/facebook_app_id"/>
<activity android:name="com.facebook.FacebookActivity"
android:configChanges=
"keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="#string/app_name" />
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<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="#string/fb_login_protocol_scheme" />
</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" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<data android:scheme="twitter-firebase-auth"/>
</intent-filter>
</activity>
pubspec.yaml packages,
flutter_facebook_auth: 4.0.0
twitter_login: 4.0.1
firebase_auth: 3.3.5
firebase_core: 1.11.0
Facebook Sign-In:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_facebook_auth/flutter_facebook_auth.dart';
var title = "";
var displayName = "";
FirebaseAuth auth = FirebaseAuth.instance;
signInWithFacebook() async {
try {
final LoginResult result = await FacebookAuth.instance.login();
switch (result.status) {
case LoginStatus.success:
final AuthCredential credential =
FacebookAuthProvider.credential(result.accessToken!.token);
if (kDebugMode) {
print(result.accessToken!.token);
}
final userCredential = await auth.signInWithCredential(credential);
if (kDebugMode) {
print(credential.signInMethod);
}
// TODO: Store user.credential!.signInMethod in SharedPref.
if (kDebugMode) {
print(userCredential.user!.displayName);
}
// boolean isLoggedIn = accessToken != null && !accessToken.isExpired();
if (kDebugMode) {
print("status: Status.Success");
}
break;
case LoginStatus.cancelled:
if (kDebugMode) {
print("status: Status.Cancelled");
}
break;
case LoginStatus.failed:
if (kDebugMode) {
print("status: Status.Failed");
}
break;
default:
if (kDebugMode) {
print("null");
}
break;
}
} catch (e) {
if (kDebugMode) {
print('Error occurred!' + e.toString());
}
}
}
Twitter Sign-In
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:twitter_login/twitter_login.dart';
FirebaseAuth _auth = FirebaseAuth.instance;
signInWithTwitter() async {
final twitterLogin = TwitterLogin(
apiKey: "XXXXXXXXXXXXXX",
apiSecretKey: "XXXXXXXXXXXXXXXXXXXX",
redirectURI: "twitter-firebase-auth://",
);
final authResult = await twitterLogin.login();
switch (authResult.status) {
case TwitterLoginStatus.loggedIn:
if (kDebugMode) {
print("status: LogIn Success");
}
final AuthCredential twitterAuthCredential =
TwitterAuthProvider.credential(
accessToken: authResult.authToken!,
secret: authResult.authTokenSecret!);
final userCredential =
await _auth.signInWithCredential(twitterAuthCredential);
if (kDebugMode) {
print("status: SignIn With Credential Success");
}
break;
case TwitterLoginStatus.cancelledByUser:
if (kDebugMode) {
print("status: Cancelled By User");
}
break;
case TwitterLoginStatus.error:
if (kDebugMode) {
print("status: Error");
}
break;
default:
if (kDebugMode) {
print("status: null");
}
}
}
Problem Solved,
Twitter Solution:
(Went through twitter_login: ^4.0.1 documentation).
<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://
Changed XML file:
<meta-data android:name="com.facebook.sdk.ApplicationId"
android:value="#string/facebook_app_id"/>
<activity android:name="com.facebook.FacebookActivity"
android:configChanges=
"keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="#string/app_name" />
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<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="#string/fb_login_protocol_scheme" />
</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" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<data android:scheme="twitter-firebase-auth"/>
</intent-filter>
</activity>
as shown below:
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="#style/NormalTheme"
/>
<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="buildyourenglish"/>
</intent-filter>
<meta-data android:name="com.facebook.sdk.ApplicationId"
android:value="#string/facebook_app_id"/>
<activity android:name="com.facebook.FacebookActivity"
android:configChanges=
"keyboard|keyboardHidden|screenLayout|screenSize|orientation"
android:label="#string/app_name" />
<activity
android:name="com.facebook.CustomTabActivity"
android:exported="true">
<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="#string/fb_login_protocol_scheme" />
</intent-filter>
</activity>
Facebook Solution:
Get the client token as shown below:
App Dashboard -> Settings -> Advanced -> Security -> Client_Token (Your facebook client token)
Go to Project_Folder -> android -> app -> src -> main -> res -> values -> value.xml (create if not existing) and paste the below code.
Similarly, Go to Project_Folder -> android -> app -> src -> main -> res -> values -> value.xml (create if not existing) and paste the below code.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">your_app_name</string>
<string name="facebook_app_id">your_facebook_app_id</string>
<string name="fb_login_protocol_scheme">your_fb_login_protocol_scheme (fb+your_facebook_app_id)</string>
<string name="facebook_client_token">your_facebook_client_token</string>
</resources>
And also generate Hash Key from the below link:
http://tomeko.net/online_tools/hex_to_base64.php
Have to give SHA1: as input. (courtesy tomeko.net and stackoverflow.com)

Android deep linking path pattern

i have this url https://myportal.net/#!/search/main/34 i need to implemnt deep linking when user click on the url its open the app.
Note: 34 number is changing in the url
i do the following but its not working
<activity
android:name="ui.Activities.SplashActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="#style/SplashScreen">
<intent-filter android:label="#string/app_name">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="${hostName}"
android:pathPattern="/.*/.*/.*/.*"
android:scheme="https" />
</intent-filter>
In Your Splash Activity
private void handleDeepLink(Intent intent) {
FirebaseDynamicLinks.getInstance()
.getDynamicLink(intent)
.addOnSuccessListener(pendingDynamicLinkData -> {
// Get deep link from result (may be null if no link is found)
Uri deepLink;
String finalLink = "";
if (pendingDynamicLinkData != null) {
deepLink = pendingDynamicLinkData.getLink();
String string = String.valueOf(deepLink);
String decodeUrl = Utils.decodeUrl(string);
String[] parts = decodeUrl.split("main/");
String part1= parts[0];
String part2= parts[1];
finalLink = part2.replaceAll(" ", "+");
Log.w("dynamiclink", finalLink);
if (!Validator.isEmptyString(finalLink)) {
getDataManager().setAuthSecret(finalLink);
}
}
getNavigator().openRegistrationActivity(finalLink);
})
.addOnFailureListener(e -> {
getNavigator().openRegistrationActivity("");
});`
}
In your Manifiest file
<intent-filter android:autoVerify="true">
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<action android:name="android.intent.action.VIEW" />
<data
android:host="nms-rx.in"
android:pathPrefix="main/"
android:scheme="https" />
</intent-filter>

Receive .vcf file from Files app in Nougat and Oreo versions to my app

I've an Android App, which works fine in Jelly Bean and Kitkat versions. The app will receive .vcf file as Intent from File Manager app using Complete Action Using option.
Now in Android Nougat and Oreo versions, Using Files app, there is an option Open with or Share will not list my App.
How to solve this? Thanks in advance.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app" >
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme.CustomOrange"
>
<activity>
...
...
</activity>
<activity
android:name=".ViewContactActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="#string/title_activity_vcf" >
<intent-filter> <!-- Handle http https requests without mimeTypes: -->
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="*" />
<data android:pathPattern="/.*\\.vcf" />
</intent-filter>
<intent-filter> <!-- Handle with mimeTypes, where the suffix is irrelevant: -->
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="*" />
<data android:mimeType="text/x-vcard" />
</intent-filter>
<intent-filter> <!-- Handle intent from a file browser app: -->
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:host="*" />
<data android:mimeType="text/x-vcard" />
</intent-filter>
</activity>
</application>
</manifest>
ViewContactActivity.java
public class ViewContactActivity
extends AppCompatActivity {
private static final String TAG = ViewContactActivity.class.getSimpleName();
public static final String VIEW_CONTACT_BY_ID = "viewContactById";
private static final int MODE_VIEW_BY_ID = 0;
private static final int MODE_VIEW_BY_FILE = 1;
private int viewContactMode;
private ActionBar mActionBar;
ContactDetails contactDetails;
String mFilePath;
private GroupInfoPickerAlertDialog mGroupInfoPickerAD;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vcf);
contactDetails = new ContactDetails(this);
Intent intent = getIntent();
Long id = intent.getLongExtra(VIEW_CONTACT_BY_ID, -1L);
Bitmap bitmap = null;
if (id != -1L) {
viewContactMode = MODE_VIEW_BY_ID;
contactDetails.collectContactDetail(id);
bitmap = BitmapFactory.decodeStream(new BufferedInputStream(contactDetails.getDisplayPhoto()));
} else {
viewContactMode = MODE_VIEW_BY_FILE;
Uri fileUri = intent.getData();
mFilePath = fileUri.getPath();
Log.d(TAG, "onCreate() - file path: " + mFilePath);
//onCreate() - file path: /path/to/file.vcf
contactDetails.readVCard2_1(fileUri);
}
setSupportActionBar((Toolbar)findViewById(R.id.toolbar));
mActionBar = getSupportActionBar();
if(mActionBar == null) {
Log.d(TAG, "onCreate() mActionBar == null");
} else {
Log.d(TAG, "onCreate() mActionBar != null");
}
mActionBar.setCustomView(null);
mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_SHOW_TITLE);
contactDetails.displayContact(bitmap);
if (viewContactMode == MODE_VIEW_BY_FILE) {
TextView textView = (TextView) findViewById(R.id.message_text_view);
textView.setText(mFilePath);// + "\n\n" + stringBuilder);
} else {
View v = (View) findViewById(R.id.message_text_view).getParent();
v.setVisibility(View.GONE);
}
}
// ....
// ....
// Other codes...
// ....
// ....
}
Your <intent-filter> elements support http, https, and file. Few "file manager" apps will use any of those on Android 7.0+, in part because the file scheme is banned.
Replace:
<intent-filter> <!-- Handle intent from a file browser app: -->
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:host="*" />
<data android:mimeType="text/x-vcard" />
</intent-filter>
with:
<intent-filter> <!-- Handle intent from a file browser app: -->
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="file" />
<data android:scheme="content" />
<data android:mimeType="text/x-vcard" />
</intent-filter>
Then, make sure that readVCard2_1() handles file and content Uri schemes, such as by calling openInputStream() on a ContentResolver to access the stream.

Receiving data from other application comes null in android

I want to make my application like Instagram i can share pictures
directly from my application , like if i select any image from
gallery and when click on share my application shows on the list of chooser
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<activity
android:name=".ui.activities.SplashActivity"
android:label="#string/app_name"
android:launchMode="singleTop"
android:screenOrientation="sensorPortrait"
android:windowSoftInputMode="adjustResize" >
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Code for receiving data from intent is
private void initActivityState() {
if (getIntent() != null){
runShareActivityIntent();
}
}
public void runShareActivityIntent(){
Intent passIntent=getIntent();
String action = passIntent.getAction();
String type = passIntent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
Uri imageUri = (Uri) passIntent.getParcelableExtra(Intent.EXTRA_STREAM);
Intent intentcall = new Intent(this, MainActivity.class);
intentcall.putExtra(KEY_TYPE,type);
intentcall.putExtra(KEY_VALUE,imageUri.toString());
startActivity(intentcall);
finish();
}
}
Now in my case when my app is in background i am able to get
String type = passIntent.getType();
and i am able to get image uri but when my app is not running( kill app ) and i
select my application from chooser i get type as null
This worked for me:
<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.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
try adding permissions to your manifest file:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
You have to change in Manifeast.xml
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="audio/*" />
</intent-filter>
In your Main activity you have to receive the intent on oncreate() method.
Intent intent = getIntent();
String action = intent.getAction();
String type = intent.getType();
if (Intent.ACTION_SEND.equals(action) && type != null) {
if (type.startsWith("image/")) {
handleSendImage(intent); // Handle single image being sent
}
else if (type.startsWith("audio/")) {
handleSendAudio(intent); // Handle single audio being sent
}
}
Now whenever you call the application using send. You can see the icon of your app. When you get the intent you can change according to your need.
I hope this may help you.

Categories

Resources