Xamarin.Forms Android App API Level range on Google Play Store - xamarin.forms

I'm trying to publish a Xamarin.Forms Android App to the Google Play Store and all is well except the API level is restricted by range. Normally apps will say API 7 and up but mine says 7.1 - 10 (see screenshots) making it "invisible" to device with Android v 11 and up. Naturally, I would like it to just say "7.1 and up"
Any suggestions would be greatly appreciated!

so this is really bizarre. The only way I can "force" it is by specifying a max SdkVersion in the manifest. Shouldn't have to do that (Not even recommended by Google). Perhaps will figure in the future:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="REMOVED" android:versionName="3.14" android:installLocation="auto" android:versionCode="17">
<uses-sdk android:minSdkVersion="26" android:targetSdkVersion="30" android:maxSdkVersion="31" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application android:label="REMOVED" android:icon="#drawable/splashLF" android:allowBackup="false" android:usesCleartextTraffic="false">
<provider android:name="android.support.v4.content.FileProvider" android:authorities="org.REMOVED.REMOVED.provider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="#xml/provider_paths" />
</provider>
</application>
</manifest>

Related

Unexpected install output: Failure [INSTALL_PARSE_FAILED+MANIFEST_MALFORMED in xamarin forms

I am getting this error
ADB0010: Mono.AndroidTools.InstallFailedException: Unexpected install output: Failure [INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: /data/app/vmdl61481456.tmp/base.apk (at Binary XML file line #44): crc6494e14b9856016c30.PNFirebaseMessagingService: Targeting S+ (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present]
After installing "Xamarin.GooglePlayServices.Base version: 71.1610.0" for the dependency of the plugin.firebasepushnotification
Target Android version 12.0 (API Level 31 - S)
Here is my menifest.xml code
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="2" android:versionName="1.0.2" package="com.healholmes" android:installLocation="preferExternal">
<uses-sdk android:minSdkVersion="24" android:targetSdkVersion="31" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application tools:replace="android:label" android:label="Heal Holmes" android:icon="#mipmap/icon_ambush" />
<queries>
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
</manifest>
The Xamarin.GooglePlayServices.Base version: 71.1610.0 is too old. According to the error message, there is a service named PNFirebaseMessagingService in your project doesn't declare the exported property.
It should be created by the nuget package you added in your project. So you can try to use the newest version of plugin.firebasepushnotification and the Xamarin.GooglePlayServices.Base.
Such as plugin.firebasepushnotification version 3.4.35 and Xamarin.GooglePlayServices.Base version 118.1.0

AndroidTV and OPEN_DOCUMENT_TREE Intent

How do I find out if the ACTION_OPEN_DOCUMENT_TREE intent is supported on Android TV?
Targeting API 32 and using the StorageVolume createOpenDocumentTreeIntent call. I can create the intent, but when I attempt to launch it, I get:
"You don't have an app that can do this message".
My app can do that on Android Phones and Tablets, just not AndroidTV. My storage related permissions are:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
For Android TV support, I have added:
<uses-feature android:name="android.software.leanback"
android:required="false" />
<uses-feature android:name="android.hardware.touchscreen"
android:required="false" />
And my TV Activity is declared as:
<activity
android:exported="true"
android:name="com.connectedway.connectedsmb.TvActivity"
android:label="#string/app_name"
android:theme="#style/Theme.Leanback">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
The app comes up on Android TV fine, but when I try to bring up a picker, it fails with the "You don't have an app..." message. I'm thinking there isn't support for this on AndroidTV, but it could be something else.
It looks like Storage Access Framework is not included in Android TV:
https://issuetracker.google.com/issues/202985150
I guess Android TV, is not quite Android.

error APT2263: unexpected element <queries> found in <manifest>

First I beg your pardon if the problem sounds silly, but I'm a newbie in Xamarin.Forms development.
I created my first app (with a lot of efforts) and I decided to use AppCenter to deploy debug releases to my testers.
Everything went fine till the last month, when I updated packages (dependencies), removed some deprecated one and pushed changes to GIT: from that moment AppCenter can't compile my app anymore, app that's compiled properly on my pc.
If I compile using v10.0 this is the error I get:
/Users/runner/work/1/s/src/StamuraApp/StamuraApp.Android/StamuraApp.Android.csproj"
(PackageForAndroid target) (1) -> (_CreateBaseApkWithAapt2 target) ->
/Users/runner/work/1/s/src/StamuraApp/StamuraApp.Android/obj/Release/android/manifest/AndroidManifest.xml(31):
error APT2263: unexpected element <queries> found in <manifest>.
[/Users/runner/work/1/s/src/StamuraApp/StamuraApp.Android/StamuraApp.Android.csproj]
If I try to compile using v11.0 I get the error:
/Users/runner/work/1/s/src/StamuraApp/StamuraApp.Android/StamuraApp.Android.csproj"
(PackageForAndroid target) (1) -> (_ResolveSdks target) ->
/Library/Frameworks/Mono.framework/External/xbuild/Xamarin/Android/Xamarin.Android.Common.targets(819,2):
error XA0000: Could not determine API level for $(TargetFrameworkVersion) of 'v11.0'.
[/Users/runner/work/1/s/src/StamuraApp/StamuraApp.Android/StamuraApp.Android.csproj]
So I understand that AppCenter can't use v11.0, but I don't know what to do with the error.
I searched other questions and most of them talk about Gradle version, but I don't know how to set it in Visual Studio.
AppCenter warns me that
Usage of Android Gradle Plugin 4.1.0+ is not supported at the moment.
Please use any of the earlier versions.
but I don't know how I can solve it.
Is there a way I can fix the issue?
EDITED
My manifest is this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="156" android:versionName="1.10.6" package="com.sefstamura.stamuraapp" android:installLocation="auto">
<uses-sdk android:minSdkVersion="23" android:targetSdkVersion="29" />
<uses-permission android:name="android.permission.INTERNET" />
<!-- Push notifications
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
****************** -->
<application android:label="StamuraApp" android:fullBackupContent="#xml/appcenter_backup_rule">
<!-- File provider -->
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="#xml/file_paths"></meta-data>
</provider>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
</manifest>
Faced the same issue when I tried to Upgrade My FormsApp to use XamarinForms to 5.0.
Please make sure to install the latest SDK versions on your machine i.e in my case Android 11[API 30]and corresponding build tool versions.
Also, upgrade the VisualStudio to the latest version where the XamarinAndroidSDK version in turn updated to pick the API level it supports.

push notifications not handled by onMessage if received when app closed using IBM MobileFirst Platform 7.0

We are using MFP 7.0.0.00-20150907-1450 and building a hybrid app targeting iOS and Android.
We're using the "send bulk messages" REST API to send broadcast messages.
We've implemented WL.Client.Push.onMessage in our app, and as long as the app is running when the notification is received, it handles the message.
If the application is closed when the notification reaches the phone, the message payload doesn't seem to get to our onMessage implementation.
This behavior is identical between iOS and Android.
I suspect there's a timing issue between our onMessage function being assigned and when the MFP framework is trying to pass the the message into our app.
Some pointers on how we might get the message handled in the case where it was received when the app was closed would be great!
(Below are some details of our setup.)
We're using Angular and had this in our main app module:
if (angular.isDefined(window.WL) && angular.isDefined(window.WL.Client.Push)) {
window.WL.Client.Push.onMessage = function (props, payload) {
console.log('Received push notification in client', JSON.stringify(props), JSON.stringify(payload));
$rootScope.$broadcast('pushNotification', props, payload);
};
}
After noticing this ("You must declare it outside any function.") in the docs for onMessage, I've moved the assignment out to the top of a JavaScript file, outside of even an IIFE. This function doesn't seem to be called, certainly there's no logging and the variable remains undefined:
var lastPushMessage;
function pushMessageRecorder(props, payload) {
lastPushMessage = {
props: props,
payload: payload
};
console.log('MFP: push received: ' + JSON.stringify(lastPushMessage, null, 2));
}
WL.Client.Push.onMessage = pushMessageRecorder;
Our security test (users don't have to log in, just have a app packaged with our keys):
<customSecurityTest name="customTests">
<test realm="wl_antiXSRFRealm" step="1" />
<test realm="wl_authenticityRealm" step="1" />
<test realm="wl_remoteDisableRealm" step="1" />
<test realm="wl_directUpdateRealm" mode="perSession" step="1" />
<test realm="wl_anonymousUserRealm" isInternalUserID="true" step="1" />
<test realm="wl_deviceNoProvisioningRealm" isInternalDeviceID="true" step="2" />
</customSecurityTest>
It looks like I've got it working.
(Mostly. If the app is not running and the user swipes away the notification, or opens the app directly from the launcher, then the notification still doesn't show up in the app.)
There were 2 things that needed doing:
the assignment to WL.Client.Push.onMessage had to be done "outside of any function"
it looks like the value for the string "app_name" in native/res/values/strings.xml is used to locate the app from the push service and make sure the message comes through
The second point still doesn't really make sense to me, but it looks like the intent-filter action android:name value in AndroidManifest.xml is built using these values:
{AndroidManifest.xml:/manifest[#package]}.{res/values/strings.xml:/resources/string[#name="app_name"]}.NOTIFICATION
Since the app_name value is used to build the notification qualifier, it can't have any spaces, which is not so nice for the name of your app that's displayed on the phone.
I've worked around that by adding another variable to the strings.xml file (although I could have just hard coded the name in the AndroidManifest.xml too, and then use that variable as my label.
Here's what's working:
res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">MyApp</string>
<string name="app_label">My App</string>
<string name="push_notification_title">My App</string>
<!-- ... -->
</resources>
AndroidManifest.xml:
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.MyProject" android:versionCode="1" android:versionName="1.0">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="19"/>
<supports-screens android:smallScreens="false" android:normalScreens="true" android:largeScreens="false"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- Push permissions -->
<permission android:name="com.MyProject.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="com.MyProject.permission.C2D_MESSAGE"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application android:label="#string/app_label" android:icon="#drawable/icon">
<meta-data android:name="com.google.android.gms.version" android:value="#integer/google_play_services_version" />
<activity android:name=".MyApp" android:label="#string/app_name" android:configChanges="orientation|keyboardHidden|screenSize" android:launchMode="singleTask" android:theme="#android:style/Theme.Translucent.NoTitleBar" android:screenOrientation="portrait" android:windowSoftInputMode="adjustPan">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="com.MyProject.MyApp.NOTIFICATION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!-- Preference Activity -->
<activity android:name="com.worklight.common.WLSettingActivity" android:label="Worklight Settings"/>
<!-- UI Activity for displaying native dialogs -->
<activity android:name="com.worklight.wlclient.ui.UIActivity"/>
<!-- Push service -->
<!-- In order to use the c2dm library, an application must declare a class with the name C2DMReceiver, in its own package, extending com.google.android.c2dm.C2DMBaseReceiver
It must also include this section in the manifest, replacing "com.google.android.apps.chrometophone" with its package name. -->
<service android:name=".GCMIntentService"/>
<service android:name=".ForegroundService"/>
<!-- Only google service can send data messages for the app. If permission is not set - any other app can generate it -->
<receiver android:name="com.worklight.androidgap.push.WLBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<!-- Receive the actual message -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<category android:name="com.MyProject"/>
</intent-filter>
<!-- Receive the registration id -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="com.MyProject"/>
</intent-filter>
</receiver>
</application>
</manifest>

SyncAdapter onPerformSync() gets called on emulator but NOT when run on physical device

I have followed Google's tutorial to use a SyncAdapter with a dummyAccount and no ContentProvider.
The tutorial is quite straight forward, and I'm aware of the limitations of using stubs for the Account and ContentProvider. I implemented it and when run on the emulator, it works wonderfully. I see the message in the Log of when it is periodically called with periodicSync(..) and when I request specifically to be called with requestSync(..).
Here is the mind-blowing part, when I run the app on my BQ 5 HD (Android 4.1.2) the SyncAdapter is never ever called. If re-installed the app, but nothing seems to be happening. I've wasted weeks with this, and I canĀ“t figure it out.
Here is the my manifestFile
<?xml version="1.0" encoding="utf-8"?>
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
....Whole bunch of activities...
<provider
android:name="com.sf.app_name.stubs.StubProvider"
android:authorities="com.sf.app_name.provider"
android:exported="false"
android:syncable="true" />
<service android:name="com.sf.app_name.stubs.AuthenticatorService"
android:exported="false">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data
android:name="android.accounts.AccountAuthenticator"
android:resource="#xml/authenticator" />
</service>
<service
android:name="com.sf.app_name.sync.SyncService"
android:exported="false"
android:process=":sync" >
<intent-filter>
<action android:name="android.content.SyncAdapter" />
</intent-filter>
<meta-data
android:name="android.content.SyncAdapter"
android:resource="#xml/syncadapter" />
</service>
</application>
authenticator.xml
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.sf.app_name"
android:userVisible="false" />
syncadapter.xml
<?xml version="1.0" encoding="utf-8"?>
<sync-adapter
xmlns:android="http://schemas.android.com/apk/res/android"
android:contentAuthority="com.sf.app_name.provider"
android:accountType="com.sf.app_name"
android:userVisible="false"
android:supportsUploading="false"
android:allowParallelSyncs="true"
android:isAlwaysSyncable="true"/>
Input is very much appreciated.
Finally figured out what's wrong thanks to ye-old-faithful Log.d(...). It's a definite noob mistake and I'm posting the answer to prevent other beginners from committing the same mistake.
In my code where I create and add the "dummyAccount" needed by the SyncAdapter framework, I did the following:
Account[] accounts = accountManager.getAccounts();
if(accounts.length == 0){
Account newAccount = new Account(ACCOUNT, ACCOUNT_TYPE);
ContentResolver.setIsSyncable(newAccount, AUTHORITY, 1);
ContentResolver.setSyncAutomatically(newAccount, AUTHORITY, true);
ContentResolver.addPeriodicSync(newAccount, AUTHORITY, new Bundle(), 40l);
accountManager.addAccountExplicitly(newAccount, null, null);
Log.d("serverSync", "ContetResolver set periodic sync");
}
The methodgetAccounts() returns all of the accounts associated to the device. Obviously it is going to return ZERO when run in the emulator because the emulator has no account associated whatsoever. So I thought it save to do it like this. I create my account of type ACCOUNT_TYPE add it, and go merrily along my way. (By the way, the reason for the addPeriodicSync on top of the setIsSyncable and setSyncAutomatially is to solve a problem that pertains to another matter.
Ofcourse, the SyncAdapter of my APP was getting called when the following was executed:
ContentResolver.requestSync(signedInAccountInstance.getDummyAccount(),
AccountManagerActivity.AUTHORITY, bundle);
They getDummyAccount(..) was gotten like so:
Account[] accounts = accountManager.getAccounts();
return accounts[0];
When this was run on the actual physical device, I should have realized that getAccounts(..) would also return (atleast) the gmail account associated to my Android device. Hence when I would do
ContentResolver.requestSync(signedInAccountInstance.getDummyAccount(),
AccountManagerActivity.AUTHORITY, bundle);
the "dummyAccount" sent would be Google's AccountType, and hence why my app's SyncAdapter would never be called. Instead, it was the gmail app's SyncAdapter who was getting called with my bundle.
How did I discover this? By printing out my dummyAccount in Log after looking into the Accounts of my physical device under Settings>Accounts, and realizing that for some reason when I executed my app on my mobile, my gmail account would display a "sync error".
All this could have been prevented by doing things correctly the first time:
Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
Where ACCOUNT_TYPE is static final variable used in my app.
// An account type, in the form of a domain name
public static final String ACCOUNT_TYPE = "com.sf.app_name";
Hope this helps save someone some headaches.

Resources