note: Not convinced that we have to actually create two flavors, i.e., if the phone feature was used in a limited fashion then seems like one could set the feature to be optional and handle the existence of the feature in the code itself. For now, however, will plow on by creating two flavors.
For phone calls, the following are important entries in the AndroidManifest.xml. How these are relevant is discussed in: http://developer.android.com/guide/topics/manifest/uses-feature-element.html.
- permission: android.permission.CALL_PHONE
- feature: android.hardware.telephony
uses-feature: name='android.hardware.bluetooth'Ok, for our application we startup with a minimal set of feature requirements:
uses-implied-feature: name='android.hardware.bluetooth' reason='requested android.permission.BLUETOOTH permission, requested android.permission.BLUETOOTH_ADMIN permission, and targetSdkVersion > 4'
uses-feature: name='android.hardware.location'
uses-implied-feature: name='android.hardware.location' reason='requested android.permission.ACCESS_COARSE_LOCATION permission, and requested android.permission.ACCESS_FINE_LOCATION permission'
uses-feature: name='android.hardware.location.gps'
uses-implied-feature: name='android.hardware.location.gps' reason='requested android.permission.ACCESS_FINE_LOCATION permission'
uses-feature: name='android.hardware.location.network'
uses-implied-feature: name='android.hardware.location.network' reason='requested android.permission.ACCESS_COARSE_LOCATION permission'
uses-feature: name='android.hardware.screen.portrait'
uses-implied-feature: name='android.hardware.screen.portrait' reason='one or more activities have specified a portrait orientation'
uses-feature: name='android.hardware.telephony'
uses-implied-feature: name='android.hardware.telephony' reason='requested a telephony permission'
uses-feature: name='android.hardware.touchscreen'
uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
uses-feature: name='android.hardware.wifi'
uses-implied-feature: name='android.hardware.wifi' reason='requested android.permission.ACCESS_WIFI_STATE permission, and requested android.permission.CHANGE_WIFI_STATE permission'
uses-feature: name='android.hardware.touchscreen'uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'We first try to have the app make a phone call (say on a menu click) with the following:
MainActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_hello) {
Snackbar
.make(findViewById(R.id.snackbarPosition), R.string.snackbar_text, Snackbar.LENGTH_LONG)
.show();
return true;
}
if (id == R.id.action_call) {
Intent phoneCallIntent = new Intent(Intent.ACTION_CALL);
phoneCallIntent.setData(Uri.parse("tel:3524748328"));
startActivity(phoneCallIntent);
}
return super.onOptionsItemSelected(item);
}
While the application compiles and even runs, clicking the Call menu item causes a fatal exception (security exception). The simple fix is to add the following to the AndroidManifest.xml file.
AndroidManifest.xml
<uses-permission android:name="android.permission.CALL_PHONE" />
The issue now is that our application now has a feature requirement of telephony and will not be available to devices without this feature (tablets).
uses-feature: name='android.hardware.telephony'uses-implied-feature: name='android.hardware.telephony' reason='requested a telephony permission'uses-feature: name='android.hardware.touchscreen'uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
To address this, we will refactor our code to first check for telephony feature ability in the code before trying to make the call.
MainActivity.java
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_hello:
Snackbar
.make(findViewById(R.id.snackbarPosition), R.string.snackbar_text, Snackbar.LENGTH_LONG)
.show();
return true;
case R.id.action_call:
if (((TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE)).getLine1Number()
== null) {
Snackbar
.make(findViewById(R.id.snackbarPosition), R.string.no_telephony, Snackbar.LENGTH_LONG)
.show();
return true;
}
Intent phoneCallIntent = new Intent(Intent.ACTION_CALL);
phoneCallIntent.setData(Uri.parse("tel:3524748328"));
startActivity(phoneCallIntent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
Then we can change up AndroidManifest.xml to allow the app to be loaded on devices without phones (had to add the READ_PHONE_STATE permission because of the phone check).
AndroidManifest.xml
<uses-feature android:name="android.hardware.telephony" android:required="false" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
With this in place, we now have the feature requirements as follows:
uses-feature-not-required: name='android.hardware.telephony'The problem, however, now is that the app still has the call menu item and dynamically changing the menu in code adds a bit more complexity (especially if one has to do it repeatedly). This is where we use multiple APK support.
uses-feature: name='android.hardware.touchscreen'
uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
Following what we have learned can make two flavors with different requirements:
telephony
uses-feature: name='android.hardware.telephony'uses-feature: name='android.hardware.touchscreen'uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'notelephony
uses-feature: name='android.hardware.touchscreen'uses-implied-feature: name='android.hardware.touchscreen' reason='default feature for all apps'
We use a single applicationId but two different versionCode entries to distinguish the two flavors. The version code was selected so that the phone code always have a higher version number (so that devices that meet all the feature sets, i.e., phones, will get the phone code).
With all this done, we go through the publishing process and create two APK files (app-notelephony-release.apk and app-telephony-release.apk): https://developer.android.com/tools/publishing/publishing_overview.html.
The result of all this work can be see in the changes: https://github.com/larkintuckerllc/HelloAndroid/commit/128adfe13bad0f20e794a467e8810e28b9411299.
No comments:
Post a Comment