I want to use the url_launcher package. This package clearly runs on Android 11 virtual device but doesn't work on real Android 11 and Android 12 devices. However, it works on 9 and 7.
Here is my build.gradle configs
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.gelir_mii"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
And here is the code block
void openWhatsapp() async {
var whatsapp = '+9033823498234';
var whatsappURL = Uri.parse(
'whatsapp://send?phone=$whatsapp&text=Merhaba, danışmanlık hakkında bilgi almak istiyorum.');
if (await canLaunchUrl(whatsappURL)) {
await launchUrl(whatsappURL);
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("WhatsApp is not installed on the device"),
),
);
}
}
Thank you so much :)
Add any URL schemes passed to canLaunchUrl as entries in your AndroidManifest.xml, otherwise it will return false in most cases starting on Android 11 (API 30) or higher. A element must be added to your manifest as a child of the root element.
<!-- Provide required visibility configuration for API level 30 and above -->
<queries>
<!-- If your app checks for SMS support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="sms" />
</intent>
<!-- If your app checks for call support -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="tel" />
</intent>
</queries>
Please look at the pub config:
https://pub.dev/packages/url_launcher
For sending the whatsapp message using your code, I hope you might have already read Readme.md section for url_launcher and have already configured the url scheme as described there.
After the readme android & iOS configuration check, if your code is not working then check for,
var whatsappURL = Uri.parse(
'whatsapp://send?phone=$whatsapp&text=Merhaba, danışmanlık hakkında bilgi almak istiyorum.');
So, check that your $whatsapp number is in format like 911234567890, if your phone number does contain any kind of special characters like, '+', ' ', '-' then remove it and format it.
Related
Recently switched our build targets to android 30 to add support for Android 11, now directions and call for phone are not working.
The documentation mentions deep linking natively and or the use of an npm package.
Would this be done natively? Or is the package an option?
https://reactnative.dev/docs/linking
Linking.canOpenURL is now returning false when checking if it is supported when running android 30.
Linking.canOpenURL(url)
.then((supported: boolean) => (supported ? Linking.openURL(url) : Alert.alert("Error", "There was an error attempting to opening the location.")))
.catch(() => Alert.alert("Error", "There was an error attempting to opening the location."));
The answer lies in a new Android security feature: Package Visibility. This works similar to iOS' LSApplicationQueriesSchemes.
Targeting Android 11 (SDK 30) requires you to update your AndroidManifest.xml and include a list of applications you're querying for. E.g. here's the code I'm using to check for Google Maps navigation in my own app. It also includes permissions for HTTP and HTTPS:
<manifest package="com.example.game">
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="geo" />
</intent>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="google.navigation" />
</intent>
</queries>
...
</manifest>
For applications matching certain criterias (such as Antivirus apps, file managers, or browser) you can use the QUERY_ALL_PACKAGES permission to allow querying any random package similar to earlier Android versions:
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
However, be aware that your application will be rejected from Google Play if you don't qualify as any of the app types listed below or uses the QUERY_ALL_PACKAGES in an unauthorized way:
Permitted use involves apps that must discover any and all installed apps on the device, for awareness or interoperability purposes may have eligibility for the permission. Permitted use includes device search, antivirus apps, file managers, and browsers. Apps granted access to this permission must comply with the User Data policies, including the Prominent Disclosure and Consent requirements, and may not extend its use to undisclosed or invalid purposes.
See Use of the broad package (App) visibility (QUERY_ALL_PACKAGES) permission
In my case I needed to add a android:host="*" to the data tag like this:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.acme">
...
<queries>
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" android:host="*" />
</intent>
</queries>
</manifest>
For Linking.canOpenURL(url) to return true for https://... URLs.
Take care if you decide to use
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
google says:
Permitted use involves apps that must discover any and all installed apps on the device, for awareness or interoperability purposes may have eligibility for the permission. Permitted use includes device search, antivirus apps, file managers, and browsers.
Apps granted access to this permission must comply with the User Data policies, including the Prominent Disclosure and Consent requirements, and may not extend its use to undisclosed or invalid purposes.
on policy update from Google Play
If you're using expo you can add this (https://github.com/chirag04/react-native-mail/pull/175/files) to your manifest file by doing the next:
Create a new file at root folder named 'android-manifest.plugin.js' with the next code on it.
const { withAndroidManifest } = require("#expo/config-plugins")
module.exports = function androiManifestPlugin(config) {
return withAndroidManifest(config, async config => {
let androidManifest = config.modResults.manifest
androidManifest["queries"].push({
"intent": [{
"action": [{
"$": {
"android:name": "android.intent.action.SEND_MULTIPLE"
}
}],
"data": [{
"$": {
"android:mimeType": "*/*"
}
}]
}]
});
return config;
})
}
Add the plugin above to your expo config file app.json
{
"expo": {
...,
"plugins": [
"./android-manifest.plugin.js"
]
}
}
Rebuild by using a eas build or you can use classic (expo build) but remember it will be deprecated soon
That's it. Your apk should have the normal manifest plus the section that was mentioned in the link above. You can also use this approach to set your manifest as your wish. You can also verify that your manifest was successfully modified by using this tool (How to view AndroidManifest.xml from APK file?)
By adding this in manifest resolved the issue
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
Thank you everyone for your support.
I am using the PackageManager to get a list of all packages installed on the user's device. This is working perfectly fine, until I switch from targetSdkVersion 29 to 30.
When I increase the targetSdkVersion from 29 to 30, the PackageManager is not returning the correct list of packages anymore (I'm making a launcher and in fact, it is barely returning any packages that can be launched).
I tried pm.getInstalledPackages(0), pm.getInstalledApplications(0) and the method for retrieving apps as indicated here. None of them worked, and all of them were working previously.
The build.gradle version settings:
compileSdkVersion 30
defaultConfig {
minSdkVersion 23
targetSdkVersion 29
}
Does anyone have an idea of what is happening here?
You need to add a declaration to your manifest in order to see other packages when targeting Android 11 (API 30): https://developer.android.com/about/versions/11/privacy/package-visibility
In particular, if you're building a launcher,
<manifest>
<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>
</queries>
...
</manifest>
would allow all results that
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
context.getPackageManager().queryIntentActivities(intent, 0);
could match to be returned.
As #ephemient pointed out in his comment of his answer, using <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/> solved it.
This is done because of the restricted access apps have from Android 11 onwards, as this article explains well.
I have a project where I am attempting to add Android Auto support. I have added the following code to my manifest as shown in the Auto documentation:
<application
....
<meta-data android:name="com.google.android.gms.car.application"
android:resource="#xml/automotive_app_desc"/>
....
<service
android:name="com.me.auto.MyMediaBrowserService"
android:exported="false">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
....
</applicaiton>
I'm also using different build flavors, defined in my gradle.build file:
defaultConfig {
applicationId "com.me"
minSdkVersion 16
//noinspection OldTargetApi
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
productFlavors {
regular {
applicationId "com.me"
}
different {
applicationId "com.meother"
}
}
When I build and install using the 'regular' flavor, android auto does not work. However, when I build and install using the 'different' flavor, everything works great. If I then change the regular applicaitonId to something else like 'com.menew', again Auto works great.
How is the applicationId in the build flavor making or breaking Android Auto functionality?
I am not absolutely sure, but I would guess this is related with the application id, e.g. you can make avoid full qualified package names by using the relative names you can use it in the manifest all all places. Check this:
<service
android:name="com.me.auto.MyMediaBrowserService" ...>
vs.
<service
android:name=".auto.MyMediaBrowserService" ...>
Make also sure that you have no hard coded packages in your code always use BuildCondig.APPLICATION_ID when you need your package name.
Looks like you have it mostly right. I would recommend these changes (based on https://developer.android.com/training/auto/audio/index.html) and see if this fixes it.
1) Remove the package name so it's not locked into one flavor. Alternatively, you can use ${applicationId} and gradle will insert the correct one.
2) Set the service to be exported (android:exported=true).
<application
....
<meta-data android:name="com.google.android.gms.car.application"
android:resource="#xml/automotive_app_desc"/>
....
<service
android:name="${applicationId}.auto.MyMediaBrowserService"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService" />
</intent-filter>
</service>
....
</applicaiton>
Did you try to create a flavorDimensions?
You can try this.
flavorDimensions "mode"
productFlavors {
regular {
dimension = "mode"
}
different {
dimension = "mode"
}
}
if you want to get the version of your application
if (BuildConfig.Flavor.contains("regular") || BuildConfig.Flavor.contains("different")) {
// Your code goes here.
}
Hope this will help.
I had an app that could detect when an android wear device disconnected by using a WearableListenerService and onPeerConnected/onPeerDisconnected.
It seems these have been deprecated, so I am now trying to use onCapabilityChanged, but I cannot get this function called. I use this in my manifest for my service. Documentation on these features are not very good.
<intent-filter>
<action android:name="com.google.android.gms.wearable.CAPABILITY_CHANGED" />
</intent-filter>
So I finally got it to work. It took a combination of things that needed to be set up, but I'll list them all.
The Gradle. You need to make sure the mobile version and the wearable version has the same application id, the same version code, the same version name, and possibly the same play-services version. This is easier to handle if you use the project gradle file to hold these values and have each module reference these values.
In the Root build.gradle file have:
ext {
TARGET_SDK_VERSION = 25
VERSION_CODE = 7
VERSION_NAME = '2.0'
COMPILE_SDK_VERSION = 25
BUILD_TOOLS_VERSION = '25.0.2'
APPLICATION_ID = "com.example.projectname"
PLAY_SERVICES_WEARABLE = 'com.google.android.gms:play-services-wearable:9.4.0'
}
In each of the module build.gradle files, these can be referenced as shown below:
apply plugin: 'com.android.application'
android {
compileSdkVersion rootProject.ext.COMPILE_SDK_VERSION
buildToolsVersion rootProject.ext.BUILD_TOOLS_VERSION
defaultConfig {
applicationId rootProject.ext.APPLICATION_ID
minSdkVersion 20
targetSdkVersion rootProject.ext.TARGET_SDK_VERSION
versionCode rootProject.ext.VERSION_CODE
versionName rootProject.ext.VERSION_NAME
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
provided 'com.google.android.wearable:wearable:2.0.1'
compile 'com.google.android.support:wearable:2.0.1'
compile rootProject.ext.PLAY_SERVICES_WEARABLE
}
The Manifest. With the new updates to the play services, a WearableListenerService must now have an intent-filter defined for each overrided function to be called by the android system. In the case of the onCapabilityChanged function, the intent filter should be defined as:
<service
android:name=".MyWearableListenerService"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="com.google.android.gms.wearable.CAPABILITY_CHANGED" />
<data android:scheme="wear" android:host="*"/>
</intent-filter>
<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<action android:name="com.google.android.gms.wearable.MESSAGE_RECEIVED" />
<data android:scheme="wear" android:host="*" android:pathPrefix="/PREF"/>
<data android:scheme="wear" android:host="*" android:pathPrefix="/start"/>
</intent-filter>
</service>
The intent-filter for onCapabilityChanged is com.google.android.gms.wearable.CAPABILITY_CHANGED. Along with that, the intent-filter also needs to be told the data scheme and host. This can simply be data android:scheme="wear" android:host="*". The pathPrefix can be omitted for this intent-filter. Notice that the intent-filter for the com.google.android.gms.wearable.DATA_CHANGED and com.google.android.gms.wearable.MESSAGE_RECEIVED needs the pathPrefix defined to be able to have their respective functions called in the service.
The capability file. In order for the onCapabilityChanged function to launch, the system needs to detect a device with a capability being connected. To do this, we must have the capability defined in an xml file in each module.
To do this, in each module, save a file named wear.xml in the res/values directory. The file must have a string array named android_wear_capabilities with items that describe the capabilities you wish your module to advertise to another device. Below is an example of a wear.xml file included in a wearable module.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="android_wear_capabilities">
<item>verify_remote_wear_app</item>
</string-array>
</resources>
First, It is important to note that the file must be named wear.xml and must be placed in the values directory. Secondly, the string-array must be named android_wear_capabilities. Also make sure that every capability in each module has a unique name.
If any of the above is not correct, then the onCapabilityChanged function will never be called, and you will be pulling your hair out in frustration.
Now, to actually tell if a device was disconnected, use the onCapabilityChanged function:
public void onCapabilityChanged(CapabilityInfo capabilityInfo) {
super.onCapabilityChanged(capabilityInfo);
if(capabilityInfo.getNodes().size() > 0){
Log.d(TAG, "Device Connected");
}else{
Log.d(TAG, "No Devices");
}
}
This function will tell you when a device has connected or disconnected, assuming only 1 device is connected at a time.
In recent versions of the Spotify Android app (3.9.0.965 at the time of writing) the Share -> Send to menu shows a bespoke list of options:
Select Recipient, Email, SMS and then a list of other apps (WhatsApp, Hangouts etc).
Is it possible for me to get my app onto that list? I'd like to be able to share a Spotify track with my app and play it.
Is it possible for me to get my app onto that list?
No, unfortunately this is not possible, even if your manifest is properly configured you cannot see your app when you choose Share -> Send to because Spotify will display only a predefined set of apps (WhatsApp, Facebook Messenger, Hangouts).
For example we have an app with the package name com.example.spotify. Add this intent-filter to the AndroidManifest.xml:
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
Run the app, but if we choose Share -> Send to the app will not appear.
Now change the applicationId to one of the whitelisted package name (com.whatsapp, com.facebook.orca, com.google.android.talk) in our build.gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.whatsapp"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
Now the application is available in the Share -> Send to context menu as if it were WhatsApp, as you can see in this screenshot:
Choosing WhatsApp our app will correctly opens and receive the intent from Spotify.
You need provide the Activity(SomeShareActivity) in the manifest and provide the IntentFilters to it
<activity android:name=".SomeShareActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="audio/*" />
<data android:mimeType="video/*" />
</intent-filter>
</activity>