diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index cbf51d06..0e160141 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -16,6 +16,8 @@ jobs:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
+ - name: Set release version
+ run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up ruby
uses: ruby/setup-ruby@v1
with:
diff --git a/README.md b/README.md
index 54e1e249..d4bf8ba6 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,21 @@
Medic Mobile Android App
========================
-
-
The medic-android application is a thin wrapper to load the [CHT Core Framework](https://github.com/medic/cht-core/) web application in a webview. This allows the application to be hardcoded to a specific CHT deployment and have a partner specific logo and display name. This app also provides some deeper integration with other android apps and native phone functions that are otherwise unavailable to webapps.
# Release notes
+## 0.7.0
+
+### Changes
+
+- [feature] [medic-android#136](https://github.com/medic/medic-android/issues/136): Add UI for prominent disclosure when requesting location permissions.
+
+### Notes
+
+The text used in the new location permission request is in the Android wrapper app itself (`medic-android`), and translated differently than [CHT Core labels](https://docs.communityhealthtoolkit.org/core/overview/translations/). Any additions or modifications to translations in `medic-android` are done in the `strings.xml` files according to the [Android localization framework](https://developer.android.com/guide/topics/resources/localization).
+
+
## 0.6.0
### Upgrade notes
@@ -64,7 +73,7 @@ To add a new brand:
1. add `productFlavors { { ... } }` in `build.gradle`
1. add icons, strings etc. in `src/`
-1. to enable automated deployments, add the `new_brand` to `.travis.yml`
+1. to enable automated deployments, add the `new_brand` to `.github/workflows/publish.yml`
# Releasing
diff --git a/build.gradle b/build.gradle
index 34f208a2..9986bc2c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -36,11 +36,11 @@ def simprintsApiKey, simprintsModuleId, simprintsUserId
def getVersionCode = {
int versionCode = 1
- if(System.env.TRAVIS == 'true' && System.env.TRAVIS_TAG && System.env.TRAVIS_TAG.startsWith('v')) {
- def versionParts = System.env.TRAVIS_TAG.split(/[^0-9]+/)
+ if(System.env.CI == 'true' && System.env.RELEASE_VERSION && System.env.RELEASE_VERSION.startsWith('v')) {
+ def versionParts = System.env.RELEASE_VERSION.split(/[^0-9]+/)
if (versionParts.length != 4 && versionParts.length != 5)
- throw new RuntimeException("Unexpected version number - should be of formatted as 'v1.2.3' or 'v1.2.3-alpha.4', but was: $System.env.TRAVIS_TAG")
+ throw new RuntimeException("Unexpected version number - should be of formatted as 'v1.2.3' or 'v1.2.3-alpha.4', but was: $System.env.RELEASE_VERSION")
versionParts = versionParts.drop(1).collect { Integer.parseInt(it) }
int alphaPart = versionParts.size() == 4 ? versionParts[3] : 99;
@@ -56,7 +56,7 @@ def getVersionCode = {
}
def getVersionName = {
- System.env.TRAVIS_TAG ?: 'SNAPSHOT'
+ System.env.RELEASE_VERSION ?: 'SNAPSHOT'
}
android {
@@ -141,12 +141,14 @@ android {
disable 'UnusedResources' // linter can't handle static imports, so just skip this test
disable 'GradleDependency' // TODO update to latest support-v4 lib and re-enable this rule
disable 'OldTargetApi'
+ disable 'MissingTranslation'
+ disable 'StringFormatCount'
warningsAsErrors true
xmlReport false
- if(System.env.TRAVIS == 'true') {
+ if(System.env.CI == 'true') {
abortOnError true
htmlReport false
textReport true
@@ -303,7 +305,11 @@ android {
abi {
enable true
reset()
- include 'armeabi-v7a', 'arm64-v8a'
+ include(
+ 'armeabi-v7a',
+ 'arm64-v8a',
+ //'x86', //--> uncomment to be able to deploy the app in Android 10+ virtual devices
+ )
universalApk false
}
}
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 95735a51..670abcba 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -11,7 +11,7 @@ platform :android do
end
lane :deploy do |options|
- version = ENV['TRAVIS_TAG'].empty? ? 'SNAPSHOT' : ENV['TRAVIS_TAG']
+ version = ENV['RELEASE_VERSION'].empty? ? 'SNAPSHOT' : ENV['RELEASE_VERSION']
package_name = options[:flavor] == 'unbranded' ? "org.medicmobile.webapp.mobile" : "org.medicmobile.webapp.mobile.#{options[:flavor]}"
supply(
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 5ad24305..002ab083 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -43,6 +43,8 @@
android:screenOrientation="portrait"/>
+
diff --git a/src/main/java/org/medicmobile/webapp/mobile/MrdtSupport.java b/src/main/java/org/medicmobile/webapp/mobile/MrdtSupport.java
index 93c58499..b3553ec8 100644
--- a/src/main/java/org/medicmobile/webapp/mobile/MrdtSupport.java
+++ b/src/main/java/org/medicmobile/webapp/mobile/MrdtSupport.java
@@ -6,7 +6,7 @@
//import org.json.JSONException;
-import static org.medicmobile.webapp.mobile.EmbeddedBrowserActivity.GRAB_MRDT_PHOTO;
+import static org.medicmobile.webapp.mobile.EmbeddedBrowserActivity.GRAB_MRDT_PHOTO_ACTIVITY_REQUEST_CODE;
import static org.medicmobile.webapp.mobile.JavascriptUtils.safeFormat;
import static org.medicmobile.webapp.mobile.MedicLog.trace;
import static org.medicmobile.webapp.mobile.MedicLog.warn;
@@ -26,14 +26,14 @@ boolean isAppInstalled() {
}
void startVerify() {
- ctx.startActivityForResult(verifyIntent(), GRAB_MRDT_PHOTO);
+ ctx.startActivityForResult(verifyIntent(), GRAB_MRDT_PHOTO_ACTIVITY_REQUEST_CODE);
}
String process(int requestCode, int resultCode, Intent i) {
trace(this, "process() :: requestCode=%s", requestCode);
switch(requestCode) {
- case GRAB_MRDT_PHOTO: {
+ case GRAB_MRDT_PHOTO_ACTIVITY_REQUEST_CODE: {
try {
byte[] data = i.getByteArrayExtra("data");
String base64data = Base64.encodeToString(data, Base64.NO_WRAP);
diff --git a/src/main/java/org/medicmobile/webapp/mobile/RequestPermissionActivity.java b/src/main/java/org/medicmobile/webapp/mobile/RequestPermissionActivity.java
new file mode 100644
index 00000000..957224ff
--- /dev/null
+++ b/src/main/java/org/medicmobile/webapp/mobile/RequestPermissionActivity.java
@@ -0,0 +1,43 @@
+package org.medicmobile.webapp.mobile;
+
+import android.os.Bundle;
+import android.view.View;
+import android.view.Window;
+import android.widget.TextView;
+
+import static org.medicmobile.webapp.mobile.MedicLog.trace;
+
+/**
+ * Shows a confirmation view that displays a "prominent" disclosure about how
+ * the user geolocation data is used, asking to confirm whether to allow the app to
+ * access the location or not.
+ *
+ * If the user accepts, a request to the API to access the location is made by the main activity,
+ * but Android will show another confirmation dialog. If the user decline the first
+ * confirmation, the request to the API is omitted and the decision recorded to avoid
+ * requesting the same next time.
+ */
+public class RequestPermissionActivity extends LockableActivity {
+
+ @Override public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ this.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.request_permission);
+ String message = getResources().getString(R.string.locRequestMessage);
+ String appName = getResources().getString(R.string.app_name);
+ TextView field = (TextView) findViewById(R.id.locMessageText);
+ field.setText(String.format(message, appName));
+ }
+
+ public void onClickOk(View view) {
+ trace(this, "onClickOk() :: user accepted to share the location");
+ setResult(RESULT_OK);
+ finish();
+ }
+
+ public void onClickNegative(View view) {
+ trace(this, ":: onClickNegative() :: user denied to share the location");
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+}
diff --git a/src/main/java/org/medicmobile/webapp/mobile/SettingsStore.java b/src/main/java/org/medicmobile/webapp/mobile/SettingsStore.java
index 48be77cd..41ac9ea3 100644
--- a/src/main/java/org/medicmobile/webapp/mobile/SettingsStore.java
+++ b/src/main/java/org/medicmobile/webapp/mobile/SettingsStore.java
@@ -45,13 +45,32 @@ void updateWithUnlockCode(String unlockCode) throws SettingsException {
ed.putString("unlock-code", unlockCode);
if(!ed.commit()) throw new SettingsException(
- "Failed to save to SharedPreferences.");
+ "Failed to save 'unlock-code' to SharedPreferences.");
}
String get(String key) {
return prefs.getString(key, null);
}
+ /**
+ * Returns true if the user has denied to provide its geolocation data.
+ * The rejection is taken from the first view with the "prominent" disclosure
+ * about the location data, not from the native dialog displayed by Android.
+ */
+ boolean hasUserDeniedGeolocation() {
+ return prefs.getBoolean("denied-geolocation", false);
+ }
+
+ /**
+ * @see #hasUserDeniedGeolocation()
+ */
+ void setUserDeniedGeolocation() throws SettingsException {
+ SharedPreferences.Editor ed = prefs.edit();
+ ed.putBoolean("denied-geolocation", true);
+ if(!ed.commit()) throw new SettingsException(
+ "Failed to save 'denied-geolocation' to SharedPreferences.");
+ }
+
static SettingsStore in(Context ctx) {
if(DEBUG) log("Loading settings for context %s...", ctx);
diff --git a/src/main/res/drawable-hdpi/location_pin.png b/src/main/res/drawable-hdpi/location_pin.png
new file mode 100644
index 00000000..0d1fd948
Binary files /dev/null and b/src/main/res/drawable-hdpi/location_pin.png differ
diff --git a/src/main/res/drawable-mdpi/location_pin.png b/src/main/res/drawable-mdpi/location_pin.png
new file mode 100644
index 00000000..94a038c8
Binary files /dev/null and b/src/main/res/drawable-mdpi/location_pin.png differ
diff --git a/src/main/res/drawable-xhdpi/location_pin.png b/src/main/res/drawable-xhdpi/location_pin.png
new file mode 100644
index 00000000..6dc7fd09
Binary files /dev/null and b/src/main/res/drawable-xhdpi/location_pin.png differ
diff --git a/src/main/res/drawable-xxhdpi/location_pin.png b/src/main/res/drawable-xxhdpi/location_pin.png
new file mode 100644
index 00000000..b74b4585
Binary files /dev/null and b/src/main/res/drawable-xxhdpi/location_pin.png differ
diff --git a/src/main/res/layout/request_permission.xml b/src/main/res/layout/request_permission.xml
new file mode 100644
index 00000000..330e7265
--- /dev/null
+++ b/src/main/res/layout/request_permission.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/res/values-bm/strings.xml b/src/main/res/values-bm/strings.xml
new file mode 100644
index 00000000..a02060c5
--- /dev/null
+++ b/src/main/res/values-bm/strings.xml
@@ -0,0 +1,8 @@
+
+
+ I bɛ yɔrɔ min na
+ %s b\'a fɛ ka dɔn aw bɛ yɔrɔ min na walasa ka aw dɛmɛ kɛnɛya sabatili baraw la. I bolo da Awɔ kan ɲɛfɛ.
+ Awɔ
+ Ayi man sɔn
+ Icône de mon emplacement
+
diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml
new file mode 100644
index 00000000..957a2a0b
--- /dev/null
+++ b/src/main/res/values-es/strings.xml
@@ -0,0 +1,8 @@
+
+
+ Acceso a la ubicación
+ %s recolecta información de la ubicación para analizar y mejorar resultados de salud en tu área. Para permitir el acceso a la ubicación selecciona \"Activar\", y luego confirma en el próximo diálogo.
+ Activar
+ No gracias
+ Ícono de mi ubicación
+
diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml
new file mode 100644
index 00000000..5a671981
--- /dev/null
+++ b/src/main/res/values-fr/strings.xml
@@ -0,0 +1,8 @@
+
+
+ Accès à la position
+ %s collecte votre position pour analyser et améliorer les services de santé dans votre région. Pour permettre l\'accès aux données de position, sélectionnez \"Activer\" et confirmez à l\'écran qui suivra.
+ Activer
+ Non merci
+ Icône de mon emplacement
+
diff --git a/src/main/res/values-hi/strings.xml b/src/main/res/values-hi/strings.xml
new file mode 100644
index 00000000..6fe10eb6
--- /dev/null
+++ b/src/main/res/values-hi/strings.xml
@@ -0,0 +1,8 @@
+
+
+ स्थान का उपयोग
+ यह ऐप आपके क्षेत्र में स्वास्थ्य परिणामों का विश्लेषण और सुधार करने के लिए स्थान डेटा एकत्र करता है। स्थान पहुंच की अनुमति देने के लिए "चालू करें" चुनें, और फिर अगले संकेत की पुष्टि करें।
+ चालू करो
+ जी नहीं, धन्यवाद
+ मेरा स्थान आइकन
+
diff --git a/src/main/res/values-in/strings.xml b/src/main/res/values-in/strings.xml
new file mode 100644
index 00000000..30a53a53
--- /dev/null
+++ b/src/main/res/values-in/strings.xml
@@ -0,0 +1,8 @@
+
+
+ Akses lokasi
+ %s mengumpulkan data lokasi untuk menganalisis dan meningkatkan hasil kesehatan di wilayah Anda. Untuk mengizinkan akses lokasi, pilih \"Nyalakan\", lalu konfirmasikan permintaan berikutnya.
+ Nyalakan
+ Tidak, terima kasih
+ Ikon lokasiku
+
diff --git a/src/main/res/values-ne/strings.xml b/src/main/res/values-ne/strings.xml
new file mode 100644
index 00000000..80f18bc2
--- /dev/null
+++ b/src/main/res/values-ne/strings.xml
@@ -0,0 +1,8 @@
+
+
+ स्थान पहुँच
+ यो अनुप्रयोगले विश्लेषण गर्दछ र तपाईंको क्षेत्रमा स्वास्थ्य परिणामहरू सुधार गर्न स्थान डाटा संकलन गर्दछ। स्थान पहुँचलाई अनुमति दिन "खोल्नुहोस्" चयन गर्नुहोस्, र त्यसपछि अर्को प्रॉम्प्ट पुष्टि गर्नुहोस्।
+ खोल्नुहोस्
+ धन्यबाद
+ मेरो स्थान आइकन
+
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 5d77ad39..279b7003 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -45,4 +45,10 @@
Choose image
Compressing image…
+
+ Location access
+ %s collects location data to analyze and improve health outcomes in your area. To permit location access select \"Turn on\", and then confirm the next prompt.
+ Turn on
+ No thanks
+ My location icon
diff --git a/src/main/res/values/styles.xml b/src/main/res/values/styles.xml
index b19f582b..6c58cfd0 100644
--- a/src/main/res/values/styles.xml
+++ b/src/main/res/values/styles.xml
@@ -10,4 +10,8 @@
- 32dp
- 32dp
+
diff --git a/src/webview/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java b/src/webview/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java
index 3158debf..fec1754b 100644
--- a/src/webview/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java
+++ b/src/webview/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java
@@ -40,12 +40,23 @@
@SuppressWarnings({ "PMD.GodClass", "PMD.TooManyMethods" })
public class EmbeddedBrowserActivity extends LockableActivity {
- /** Any activity result with all 3 low bits set is _not_ a simprints result. */
+ /**
+ * Any activity result with all 3 low bits set is _not_ a simprints result.
+ *
+ * The following block of bit-shifted integers are intended for use in the subsystem seen
+ * in the onActivityResult below. These integers respect the reserved block of integers
+ * which are used by simprints. Simprint intents are started in the webapp where a matching
+ * bitmask is used to respect the scheme on that side of things.
+ * */
private static final int NON_SIMPRINTS_FLAGS = 0x7;
- static final int GRAB_PHOTO = (0 << 3) | NON_SIMPRINTS_FLAGS;
- static final int GRAB_MRDT_PHOTO = (1 << 3) | NON_SIMPRINTS_FLAGS;
+ static final int GRAB_PHOTO_ACTIVITY_REQUEST_CODE = (0 << 3) | NON_SIMPRINTS_FLAGS;
+ static final int GRAB_MRDT_PHOTO_ACTIVITY_REQUEST_CODE = (1 << 3) | NON_SIMPRINTS_FLAGS;
+ static final int DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE = (2 << 3) | NON_SIMPRINTS_FLAGS;
- private final static int ACCESS_FINE_LOCATION_PERMISSION_REQUEST = (int)(Math.random() * 1000);
+ // Arbitrarily selected value
+ private static final int ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE = 7038678;
+
+ private static final String[] LOCATION_PERMISSIONS = { Manifest.permission.ACCESS_FINE_LOCATION };
private static final ValueCallback IGNORE_RESULT = new ValueCallback() {
public void onReceiveValue(String result) { /* ignore */ }
@@ -166,19 +177,39 @@ protected void onStart() {
@Override protected void onActivityResult(int requestCode, int resultCode, Intent i) {
try {
- trace(this, "onActivityResult() :: requestCode=%s, resultCode=%s", requestCode, resultCode);
+ trace(this, "onActivityResult() :: requestCode=%s, resultCode=%s",
+ requestCodeToString(requestCode), resultCode);
if((requestCode & NON_SIMPRINTS_FLAGS) == NON_SIMPRINTS_FLAGS) {
switch(requestCode) {
- case GRAB_PHOTO:
+ case GRAB_PHOTO_ACTIVITY_REQUEST_CODE:
photoGrabber.process(requestCode, resultCode, i);
return;
- case GRAB_MRDT_PHOTO:
+ case GRAB_MRDT_PHOTO_ACTIVITY_REQUEST_CODE:
String js = mrdt.process(requestCode, resultCode, i);
trace(this, "Execing JS: %s", js);
evaluateJavascript(js);
return;
+ case DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE:
+ // User accepted or denied to allow the app to access
+ // location data in RequestPermissionActivity
+ if (resultCode == RESULT_OK) { // user accepted
+ // Request to Android location data access
+ ActivityCompat.requestPermissions(
+ this,
+ LOCATION_PERMISSIONS,
+ ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE);
+ } else if (resultCode == RESULT_CANCELED) { // user rejected
+ try {
+ this.locationRequestResolved();
+ settings.setUserDeniedGeolocation();
+ } catch (SettingsException e) {
+ error(e, "Error recording negative to access location");
+ }
+ }
+ return;
default:
- trace(this, "onActivityResult() :: no handling for requestCode=%s", requestCode);
+ trace(this, "onActivityResult() :: no handling for requestCode=%s",
+ requestCodeToString(requestCode));
}
} else {
String js = simprints.process(requestCode, i);
@@ -187,10 +218,21 @@ protected void onStart() {
}
} catch(Exception ex) {
String action = i == null ? null : i.getAction();
- warn(ex, "Problem handling intent %s (%s) with requestCode=%s & resultCode=%s", i, action, requestCode, resultCode);
+ warn(ex, "Problem handling intent %s (%s) with requestCode=%s & resultCode=%s",
+ i, action, requestCodeToString(requestCode), resultCode);
}
}
+ private String requestCodeToString(int requestCode) {
+ if (requestCode == ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE) {
+ return "ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE";
+ }
+ if (requestCode == DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE) {
+ return "DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE";
+ }
+ return String.valueOf(requestCode);
+ }
+
//> ACCESSORS
SimprintsSupport getSimprintsSupport() {
return this.simprints;
@@ -305,22 +347,33 @@ private void setUpUiClient(WebView container) {
public boolean getLocationPermissions() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PERMISSION_GRANTED) {
- trace(this, "PERMISSIONS GRANTED");
+ trace(this, "getLocationPermissions() :: already granted");
return true;
}
- String[] permissions = { Manifest.permission.ACCESS_FINE_LOCATION };
- ActivityCompat.requestPermissions(this, permissions, ACCESS_FINE_LOCATION_PERMISSION_REQUEST);
+ if (settings.hasUserDeniedGeolocation()) {
+ trace(this, "getLocationPermissions() :: user has previously denied to share location");
+ this.locationRequestResolved();
+ return false;
+ }
+ trace(this, "getLocationPermissions() :: location not granted before, requesting access...");
+ startActivityForResult(
+ new Intent(this, RequestPermissionActivity.class),
+ DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE);
return false;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (requestCode != ACCESS_FINE_LOCATION_PERMISSION_REQUEST) {
+ if (requestCode != ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE) {
return;
}
- String javaScript = "angular.element(document.body).injector().get('AndroidApi').v1.locationPermissionRequestResolved();";
- evaluateJavascript(javaScript);
+ locationRequestResolved();
+ }
+
+ public void locationRequestResolved() {
+ evaluateJavascript(
+ "angular.element(document.body).injector().get('AndroidApi').v1.locationPermissionRequestResolved();");
}
@SuppressLint("SetJavaScriptEnabled")
diff --git a/src/webview/java/org/medicmobile/webapp/mobile/PhotoGrabber.java b/src/webview/java/org/medicmobile/webapp/mobile/PhotoGrabber.java
index 79da48a8..8a8425db 100644
--- a/src/webview/java/org/medicmobile/webapp/mobile/PhotoGrabber.java
+++ b/src/webview/java/org/medicmobile/webapp/mobile/PhotoGrabber.java
@@ -17,7 +17,7 @@
import static android.provider.MediaStore.ACTION_IMAGE_CAPTURE;
import static com.mvc.imagepicker.ImagePicker.getImageFromResult;
import static com.mvc.imagepicker.ImagePicker.getPickImageIntent;
-import static org.medicmobile.webapp.mobile.EmbeddedBrowserActivity.GRAB_PHOTO;
+import static org.medicmobile.webapp.mobile.EmbeddedBrowserActivity.GRAB_PHOTO_ACTIVITY_REQUEST_CODE;
import static org.medicmobile.webapp.mobile.MedicLog.log;
import static org.medicmobile.webapp.mobile.MedicLog.trace;
import static org.medicmobile.webapp.mobile.MedicLog.warn;
@@ -112,14 +112,14 @@ public void run() {
}
private void takePhoto() {
- a.startActivityForResult(cameraIntent(), GRAB_PHOTO);
+ a.startActivityForResult(cameraIntent(), GRAB_PHOTO_ACTIVITY_REQUEST_CODE);
}
private void pickImage() {
trace(this, "picking image intent");
Intent i = getPickImageIntent(a, a.getString(R.string.promptChooseImage));
trace(this, "starting activity :: %s", i);
- a.startActivityForResult(i, GRAB_PHOTO);
+ a.startActivityForResult(i, GRAB_PHOTO_ACTIVITY_REQUEST_CODE);
}
private boolean canStartCamera() {
diff --git a/src/xwalk/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java b/src/xwalk/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java
index 622ddb4e..457a1869 100644
--- a/src/xwalk/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java
+++ b/src/xwalk/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java
@@ -44,12 +44,23 @@
@SuppressWarnings({ "PMD.GodClass", "PMD.TooManyMethods" })
public class EmbeddedBrowserActivity extends LockableActivity {
- /** Any activity result with all 3 low bits set is _not_ a simprints result. */
+ /**
+ * Any activity result with all 3 low bits set is _not_ a simprints result.
+ *
+ * The following block of bit-shifted integers are intended for use in the subsystem seen
+ * in the onActivityResult below. These integers respect the reserved block of integers
+ * which are used by simprints. Simprint intents are started in the webapp where a matching
+ * bitmask is used to respect the scheme on that side of things.
+ * */
private static final int NON_SIMPRINTS_FLAGS = 0x7;
- static final int GRAB_PHOTO = (0 << 3) | NON_SIMPRINTS_FLAGS;
- static final int GRAB_MRDT_PHOTO = (1 << 3) | NON_SIMPRINTS_FLAGS;
+ static final int GRAB_PHOTO_ACTIVITY_REQUEST_CODE = (0 << 3) | NON_SIMPRINTS_FLAGS;
+ static final int GRAB_MRDT_PHOTO_ACTIVITY_REQUEST_CODE = (1 << 3) | NON_SIMPRINTS_FLAGS;
+ static final int DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE = (2 << 3) | NON_SIMPRINTS_FLAGS;
- private final static int ACCESS_FINE_LOCATION_PERMISSION_REQUEST = (int)Math.random();
+ // Arbitrarily selected value
+ private static final int ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE = 7038678; // Arbitrarily selected value
+
+ private static final String[] LOCATION_PERMISSIONS = { Manifest.permission.ACCESS_FINE_LOCATION };
private static final ValueCallback IGNORE_RESULT = new ValueCallback() {
public void onReceiveValue(String result) { /* ignore */ }
@@ -172,19 +183,39 @@ public void onReceiveValue(String result) {
@Override protected void onActivityResult(int requestCode, int resultCode, Intent i) {
try {
- trace(this, "onActivityResult() :: requestCode=%s, resultCode=%s", requestCode, resultCode);
+ trace(this, "onActivityResult() :: requestCode=%s, resultCode=%s",
+ requestCodeToString(requestCode), resultCode);
if((requestCode & NON_SIMPRINTS_FLAGS) == NON_SIMPRINTS_FLAGS) {
switch(requestCode) {
- case GRAB_PHOTO:
+ case GRAB_PHOTO_ACTIVITY_REQUEST_CODE:
photoGrabber.process(requestCode, resultCode, i);
return;
- case GRAB_MRDT_PHOTO:
+ case GRAB_MRDT_PHOTO_ACTIVITY_REQUEST_CODE:
String js = mrdt.process(requestCode, resultCode, i);
trace(this, "Execing JS: %s", js);
evaluateJavascript(js);
return;
+ case DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE:
+ // User accepted or denied to allow the app to access
+ // location data in RequestPermissionActivity
+ if (resultCode == RESULT_OK) { // user accepted
+ // Request to Android location data access
+ ActivityCompat.requestPermissions(
+ this,
+ LOCATION_PERMISSIONS,
+ ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE);
+ } else if (resultCode == RESULT_CANCELED) { // user rejected
+ try {
+ this.locationRequestResolved();
+ settings.setUserDeniedGeolocation();
+ } catch (SettingsException e) {
+ error(e, "Error recording negative to access location");
+ }
+ }
+ return;
default:
- trace(this, "onActivityResult() :: no handling for requestCode=%s", requestCode);
+ trace(this, "onActivityResult() :: no handling for requestCode=%s",
+ requestCodeToString(requestCode));
}
} else {
String js = simprints.process(requestCode, i);
@@ -193,8 +224,19 @@ public void onReceiveValue(String result) {
}
} catch(Exception ex) {
String action = i == null ? null : i.getAction();
- warn(ex, "Problem handling intent %s (%s) with requestCode=%s & resultCode=%s", i, action, requestCode, resultCode);
+ warn(ex, "Problem handling intent %s (%s) with requestCode=%s & resultCode=%s",
+ i, action, requestCodeToString(requestCode), resultCode);
+ }
+ }
+
+ private String requestCodeToString(int requestCode) {
+ if (requestCode == ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE) {
+ return "ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE";
}
+ if (requestCode == DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE) {
+ return "DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE";
+ }
+ return String.valueOf(requestCode);
}
//> ACCESSORS
@@ -324,21 +366,32 @@ public void onGeolocationPermissionsShowPrompt(
public boolean getLocationPermissions() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PERMISSION_GRANTED) {
+ trace(this, "getLocationPermissions() :: already granted");
return true;
}
-
- String[] permissions = { Manifest.permission.ACCESS_FINE_LOCATION };
- ActivityCompat.requestPermissions(this, permissions, ACCESS_FINE_LOCATION_PERMISSION_REQUEST);
+ if (settings.hasUserDeniedGeolocation()) {
+ trace(this, "getLocationPermissions() :: user has previously denied to share location");
+ this.locationRequestResolved();
+ return false;
+ }
+ trace(this, "getLocationPermissions() :: location not granted before, requesting access...");
+ startActivityForResult(
+ new Intent(this, RequestPermissionActivity.class),
+ DISCLOSURE_LOCATION_ACTIVITY_REQUEST_CODE);
return false;
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- if (requestCode != ACCESS_FINE_LOCATION_PERMISSION_REQUEST) {
+ if (requestCode != ACCESS_FINE_LOCATION_PERMISSION_REQUEST_CODE) {
return;
}
- String javaScript = "angular.element(document.body).injector().get('AndroidApi').v1.locationPermissionRequestResolved();";
- evaluateJavascript(String.format(javaScript));
+ locationRequestResolved();
+ }
+
+ public void locationRequestResolved() {
+ evaluateJavascript(
+ String.format("angular.element(document.body).injector().get('AndroidApi').v1.locationPermissionRequestResolved();"));
}
@SuppressLint("SetJavaScriptEnabled")
diff --git a/src/xwalk/java/org/medicmobile/webapp/mobile/PhotoGrabber.java b/src/xwalk/java/org/medicmobile/webapp/mobile/PhotoGrabber.java
index 5d9a3110..ea805bcb 100644
--- a/src/xwalk/java/org/medicmobile/webapp/mobile/PhotoGrabber.java
+++ b/src/xwalk/java/org/medicmobile/webapp/mobile/PhotoGrabber.java
@@ -17,7 +17,7 @@
import static android.provider.MediaStore.ACTION_IMAGE_CAPTURE;
import static com.mvc.imagepicker.ImagePicker.getImageFromResult;
import static com.mvc.imagepicker.ImagePicker.getPickImageIntent;
-import static org.medicmobile.webapp.mobile.EmbeddedBrowserActivity.GRAB_PHOTO;
+import static org.medicmobile.webapp.mobile.EmbeddedBrowserActivity.GRAB_PHOTO_ACTIVITY_REQUEST_CODE;
import static org.medicmobile.webapp.mobile.MedicLog.log;
import static org.medicmobile.webapp.mobile.MedicLog.trace;
import static org.medicmobile.webapp.mobile.MedicLog.warn;
@@ -105,12 +105,12 @@ public void run() {
}
private void takePhoto() {
- a.startActivityForResult(cameraIntent(), GRAB_PHOTO);
+ a.startActivityForResult(cameraIntent(), GRAB_PHOTO_ACTIVITY_REQUEST_CODE);
}
private void pickImage() {
Intent i = getPickImageIntent(a, a.getString(R.string.promptChooseImage));
- a.startActivityForResult(i, GRAB_PHOTO);
+ a.startActivityForResult(i, GRAB_PHOTO_ACTIVITY_REQUEST_CODE);
}
private boolean canStartCamera() {