Help With Overriding / Customizing Android Manifest

Hey all, my app with it’s installed cordova plugins results in these permissions in my AndroidManifest.xml:

<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.location.gps" />

I want my app to be able to run on ChromeBooks, and it has been suggested that I may need the following resultant lines in my manifest:

<uses-feature android:name="android.hardware.touchscreen" required="false" />
<uses-feature android:name="android.hardware.location.gps" required="false" />

So how might I achieve that result most easily? I’ve already added cordova-custom-config in pursuit of this goals, added xmlns:android="http://schemas.android.com/apk/res/android" to the widget element of config.xml, and added the following to the <platform name="android"> section of config.xml:

<config-file parent="/*" target="AndroidManifest.xml">
   <supports-screens android:largeScreens="true" android:smallScreens="true" android:xlargeScreens="true" />
</config-file>

Any help / advice would be greatly appreciated!

1 Like

You can just add the <uses-feature/> elements to your existing <config-file> block:

<config-file parent="/*" target="AndroidManifest.xml">
  <supports-screens android:largeScreens="true" android:smallScreens="true" android:xlargeScreens="true" />
  <uses-feature android:name="android.hardware.touchscreen" required="false" />
  <uses-feature android:name="android.hardware.location.gps" required="false" />
</config-file>
1 Like

@dpa99c1 thanks for the suggestion. I tried adding this and I get build errors now, even after adding mode="replace" to the config-file tag:

Error: /home/vic/git/myionic2app/platforms/android/gradlew: Command failed with exit code 1 Error output:
/home/vic/git/myionic2app/platforms/android/AndroidManifest.xml:30:5-66 Error:
	Element uses-feature#android.hardware.location.gps at AndroidManifest.xml:30:5-66 duplicated with element declared at AndroidManifest.xml:28:5-83
/home/vic/git/myionic2app/platforms/android/AndroidManifest.xml Error:
	Validation failed, exiting

FAILURE: Build failed with an exception.

After build fails, my AndroidManifest.xml file in platforms/android has this in it:

<uses-feature android:name="android.hardware.location.gps" required="false" />
<uses-feature android:name="android.hardware.touchscreen" required="false" />
<uses-feature android:name="android.hardware.location.gps" />

Based on grep’ing, this is almost certainly due to the GeoLocation (i.e. cordova-plugin-geolocation) plugin. What to do…?

Cordova hook.

  1. In root folder of your app create hooks folder if you don’t have it already
  2. In hooks create after_prepare folder
  3. In after_prepare create 030_remove_permissions.js
  4. Create script to manage your permission.

For example this is my script that is removing some permissions:

#!/usr/bin/env node

//
// This hook removes specific permissions from the AndroidManifest.xml
// The AndroidManifest is re-generated during the prepare stage,
// so this must be run on the "after_prepare" hook.
//


// Configure the permissions to be forcefully removed.
// NOTE: These permissions will be removed regardless of how many plugins
//       require the permission. You can check the permission is only required
//       by the plugin you *think* needs it, by looking at the "count" shown in
//       your /plugins/android.json file.
//       If the count is more than 1, you should search through
//       the /plugins//plugin.xml files for <uses-permission> tags.

var fs = require('fs');

if(fs.existsSync('platforms/android')) {
  const PERMISSIONS_TO_REMOVE = ["RECORD_AUDIO", "MODIFY_AUDIO_SETTINGS", "READ_PHONE_STATE"],
    MANIFEST = 'platforms/android/AndroidManifest.xml';

  manifestLines = fs.readFileSync(MANIFEST).toString().split('\n'),
    newManifestLines = [];

  const permissions_regex = PERMISSIONS_TO_REMOVE.join('|');

  manifestLines.forEach(function(line) {
    if(!line.match(permissions_regex)) {
      newManifestLines.push(line);
    }
  });

  fs.writeFileSync(MANIFEST, newManifestLines.join('\n'));
}

Can you from this figure out how to achieve what you need?

1 Like

Thanks @sava999 yea I think I can take a crack with this guidance! I’ll report back.

So good news and bad news. Good news is, using a hook worked to alter the Manifest. It ends up looking like this (inside the manifest tag):

    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application android:hardwareAccelerated="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:supportsRtl="true">
        <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:windowSoftInputMode="adjustResize">
            <intent-filter android:label="@string/launcher_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="cordova.plugins.Diagnostic$LocationProviderChangedReceiver">
            <intent-filter>
                <action android:name="android.location.PROVIDERS_CHANGED" />
            </intent-filter>
        </receiver>
        <receiver android:name="cordova.plugins.Diagnostic$NFCStateChangedReceiver">
            <intent-filter>
                <action android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
            </intent-filter>
        </receiver>
    </application>
    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-feature android:name="android.hardware.location.gps" required="false" />
    <uses-feature android:name="android.hardware.touchscreen" required="false" />

… the bad news is that after signing, aligning, uploading this APK to the Play Console, and rolling it out to my closed Beta, chromebooks are still in the unsupported list under “Manage devices”…

OK, I went all out and removed all uses-permission elements (besides android.permission.INTERNET), my Manifest now looks like this:

    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:resizeable="true" android:smallScreens="true" android:xlargeScreens="true" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application android:hardwareAccelerated="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:supportsRtl="true">
        <activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale" android:label="@string/activity_name" android:launchMode="singleTop" android:name="MainActivity" android:theme="@android:style/Theme.DeviceDefault.NoActionBar" android:windowSoftInputMode="adjustResize">
            <intent-filter android:label="@string/launcher_name">
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="cordova.plugins.Diagnostic$LocationProviderChangedReceiver">
            <intent-filter>
                <action android:name="android.location.PROVIDERS_CHANGED" />
            </intent-filter>
        </receiver>
        <receiver android:name="cordova.plugins.Diagnostic$NFCStateChangedReceiver">
            <intent-filter>
                <action android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
            </intent-filter>
        </receiver>
    </application>
    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="25" />
    <uses-feature android:name="android.hardware.location.gps" required="false" />
    <uses-feature android:name="android.hardware.touchscreen" required="false" />
    <uses-feature android:name="android.hardware.location.network" required="false" />
    <uses-feature android:name="android.hardware.wifi" required="false" />

… and still Play Console says Chromebook is unsupported. What the heck?

1 Like

Sorry, I didn’t worked with Chromebooks so I don’t know what you need to do to support it.

Any luck with this? I would also like our Ionic app usable on Chromebooks.