Sharing a track from the Spotify Android app with my app - android

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>

Related

Why Doesn't Work This Package On Flutter?

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.

Handling DropBox with different Build Variant

I'm facing an issue with dropbox in my 2 apps.
in fact I have 2 build variants and to be able to handle different secrets for both apps I use Gradle to inject the value into the manifest but for dropbox, i always have this error
The installation did not succeed.
The application could not be installed: INSTALL_FAILED_CONFLICTING_PROVIDER
what I did is I added in the manifest dropboxAppKey in the scheme attribute
<activity
android:name="com.dropbox.core.android.AuthActivity"
android:configChanges="orientation|keyboard"
android:launchMode="singleTask"
>
<intent-filter>
<data android:scheme="db-${dropboxAppKey}" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
and in Gradle i inject the app key corresponding to each flavor
App1 {
manifestPlaceholders = [dropboxAppKey : "123456"]
}
App2 {
manifestPlaceholders = [dropboxAppKey : "654321"]
}
but I got always the same error INSTALL_FAILED_CONFLICTING_PROVIDER
It sounds like you need a distinct app key for each variant (so, a total of 4 app keys in this case; 2 per app) in order to disambiguate each app variant.
Open a Dropbox API ticket from the account that owns the apps and we can add an extra app key/secret to each of your apps:
https://www.dropbox.com/developers/contact
That way, you can use a distinct key in each variant.

TWA: trying to do seamless navigation between domains and no luck

I am trying to create a TWA app in Android Studio that might be working with all our domains and provide seamless navigation between them, but ran out of ideas.
The problem is this - when browsing within one domain all is fine, the "back" button on the phone returns to a previously browsed page. But as soon as you switch to another domain, it looks like the app re-initializes, and the "back" button does not lead back to the previous domain's page. Instead, it minifies the app...
All domains contain Digital Asset files in respective folders, fingerprints are ok, app works perfectly with each individual domain but keeps acting as you've just opened it upon trying to pass from one domain to another via a link.
All domains are added in separate <data> tags under intent-filter with action.view and categories DEFAULT and BROWSABLE in manifest.
Tried adding relations in asset statements to every one of them but no luck getting desired result.
Maybe someone could make it to work? Any advice would be appreciated!
Thanks!
Chunks of my silly newb code
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ru.domain.newsapp">
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true">
<meta-data
android:name="asset_statements"
android:resource="#string/asset_statements" />
<activity
android:name="android.support.customtabs.trusted.LauncherActivity"
android:theme="#style/Theme.LauncherActivity"
android:label="#string/app_name">
<meta-data
android:name="android.support.customtabs.trusted.DEFAULT_URL"
android:value="https://sub1.domain.ru/" />
<meta-data
android:name="android.support.customtabs.trusted.STATUS_BAR_COLOR"
android:resource="#color/colorPrimary" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</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="sub1.domain.ru"/>
<data android:scheme="https" android:host="sub2.domain.ru"/>
<data android:scheme="https" android:host="sub3.domain.ru"/>
</intent-filter>
</activity>
</application>
</manifest>
res\values\strings.xml
<resources>
<string name="app_name">MyApp</string>
<string name="asset_statements">
[{
\"relation\": [\"delegate_permission/common.handle_all_urls\"],
\"target\": {
\"namespace\": \"web\",
\"site\": \"https://sub1.domain.ru\"}
},{
\"relation\": [\"delegate_permission/common.handle_all_urls\"],
\"target\": {
\"namespace\": \"web\",
\"site\": \"https://sub2.domain.ru\"}
},{
\"relation\": [\"delegate_permission/common.handle_all_urls\"],
\"target\": {
\"namespace\": \"web\",
\"site\": \"https://sub3.domain.ru\"}
}]
</string>
</resources>
res\values\styles.xml
<resources>
<!-- Base application theme. -->
<style name="Theme.LauncherActivity" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowAnimationStyle">#null</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:windowBackground">#android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
</resources>
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "ru.domain.newsapp"
minSdkVersion 16
targetSdkVersion 28
versionCode 2
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
signingConfig signingConfigs.release
}
buildTypes {
release {
minifyEnabled false
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'com.android.support:appcompat-v7:+'
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.github.GoogleChrome.custom-tabs-client:customtabs:91b4a1270b'
}
UPD
Added code from the files
Also seems like this behavior I was talking about is not a re-init but an overlay with another subdomain content over default that is opened but without action bar like in installed PWA and this leads to the problem that this overlay cannot be closed to return to the previous domain... And I really hoped that this might act like a normal browser tab and open every link in the same window...
Thanks to Google guys problem has been solved.
To be able to open links on other trusted domains inside the TWA app window and as a seamless transition in one tab (without additional overlay) I needed to use android.support.customtabs.trusted.ADDITIONAL_TRUSTED_ORIGINS and pass a string-array to in with required additional trusted domain as well as keeping only main one in intent with
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE"/>
There is still a bug that needs to be solved since additional trusted origins still show URL bar but I hope it happens soon.
Reference on this in android-browser-helper's github
https://github.com/GoogleChrome/android-browser-helper/commit/24eefc89e69532d7f57fc83533886c9d762a41eb
https://github.com/GoogleChrome/android-browser-helper/commit/a1507153b420da6a3191fa23c64c5a0c06036e06
UPD
New version of android-browser-helper (1.2.0) finally has this issue fixed and multi-domain now is working as intended (additional trusted origins are verified correctly). Please pay attention to the changes in calling additional trusted origins array as stated in Demo within android-browser-helper.

How to detect when android wear device gets disconnected?

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.

Android deeplink per flavor

I have multiple deeplink schemes for different app flavors. I have a backend which sends a different scheme per app. If all of them are installed on the same device, all of them will be able to parse the deeplink sent. So when all three are installed and the deeplink for app2 is called. All apps are able to catch it, but only app2 can properly process it in the app and should be the only one able catch it.
Flavors defined in my .gradle file
productFlavors {
app1{
applicationId "com.apps.app1"
}
app2{
applicationId "com.apps.app2"
}
app3{
applicationId "com.apps.app3"
}
}
The intent filter I use to catch the deeplinks in my manifest.
<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:pathPrefix="/"
android:scheme="app1" />
<data
android:pathPrefix="/"
android:scheme="app2" />
<data
android:pathPrefix="/"
android:scheme="app3" />
</intent-filter>
Is there a way to make a deeplink only catch-able by one flavor?
For people visiting this in 2019: You can use manifestPlaceholders per flavor.
In your case:
productFlavors {
app1{
applicationId "com.apps.app1"
manifestPlaceholders.scheme = "app1"
}
app2{
applicationId "com.apps.app2"
manifestPlaceholders.scheme = "app2"
}
app3{
applicationId "com.apps.app3"
manifestPlaceholders.scheme = "app3"
}
}
and then use it in your manifest:
<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:pathPrefix="/"
android:scheme="${scheme}" />
You have to provide each product flavor a manifest file of their own, within which you can specify distinct URI pattern for deep links.
You can refer to Configure Build Variants and
Merge Multiple Manifest Files for details about how to achieve that, and more things about building app for product flavor.

Categories

Resources