How to detect when android wear device gets disconnected? - android

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.

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.

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.

HCE service and BIND_NFC_SERVICE permission

First of all I'm fairly new to programming Android apps. My programing language is Java. I searched for a solution on the web and asked other programmers with no solution.
Situation:
I have developed an application that uses the HCE service. This application is triggered by another device and data is transferred using ISO 7816-4 APDU commands. Everything is working as expected and the app is selected using an AID.
Problem:
The app was developed on a NEXUS 4 with Android 5.1.1 running. By the time I got newer NFC enabled phones on my desk to test the app.
All of them are Android 6 or newer. Non of them is working with my app. It seems that the permission BIND_NFC_SERVICE is not granted. Since the way permissions are handled changed since Android 5 to 6 update I implemented the new method mentioned by Android developer request app permissions (I also checked the related posts here). The permission is just denied with no further information. On the NFC interface the APDU trying to select the app by its AID is returned with 6999 meaning the operation is not allowed.
On a rooted phone I can see that the permission is denied and I tried to grant it manually. But after every start of the app it is denied again and the app can never use it.
Seems like the HCE service itself has to ask for the permission, but how?
Below you can find my AndroidManifest.xml and the build.gradle. I also tried to change to a higher API level.
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.XXXXXXXX.hce_tests">
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<uses-feature
android:name="android.hardware.nfc.hce"
android:required="true" />
<uses-permission android:name="android.permission.BIND_NFC_SERVICE"/>
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="#xml/nfc_tech_filter" />
</activity>
<service
android:name=".MyHostApduService" android:permission="android.permission.BIND_NFC_SERVICE">
<intent-filter>
<action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE" />
</intent-filter>
<meta-data
android:name="android.nfc.cardemulation.host_apdu_service"
android:resource="#xml/apduservice" />
</service>
<activity android:name=".AutoTuneInterface"></activity>
</application>
</manifest>
build.gradle
android {
compileSdkVersion 23
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.example.XXXXXXXX.hce_tests"
minSdkVersion 19
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner
"android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-
core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
testCompile 'junit:junit:4.12'
implementation project(':GraphView')
}
Thanks for your help!
BIND_NFC_SERVICE Permission
You are not supposed to request the permission BIND_NFC_SERVICE from your app. This permission is reserved for the NFC system service and this service should be the only application that holds that permission.
Instead, your app needs to enforce this permission when an application tries to bind to your HCE service component. You already do that with
<service android:name=".MyHostApduService"
android:permission="android.permission.BIND_NFC_SERVICE"
That way, Android prevents injection of APDUs into your HCE service by other (malicious) apps, since no (malicious) app could bind to your HCE service.
Error 6999
Why Android does not detect your HCE service and returns 6999 to indicate that the AID was not found, is a trickier question. I could think of a few reasons:
Your service is not explicitly marked as exported (add the attribute android:exported="true" to the service component). However, this should not matter since the HCE service also declares an intent filter which implicitly sets the exported attribute true.
If your app is flagged as a payment app (in apduservice.xml), you will need to make sure that it is the selected payment app ("Tap & pay" in the Settings app).
Some devices do not seem to support HCE and a secure element in parallel and have a switch to choose between the secure element and HCE somewhere in the Settings app (probably also within the Tap & pay settings).

Android Auto Setup with Gradle Build Flavors

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.

Sharing a track from the Spotify Android app with my app

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>

Categories

Resources