Skip to content

Commit

Permalink
Add dialog with prominent disclosure to request location
Browse files Browse the repository at this point in the history
Adds an interstitial dialog to explain why and how the app uses location data.

#136
  • Loading branch information
mrsarm authored Jan 12, 2021
1 parent 6705216 commit a4c31ab
Show file tree
Hide file tree
Showing 25 changed files with 372 additions and 50 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
Medic Mobile Android App
========================

<a href="https://travis-ci.org/medic/medic-android"><img src="https://travis-ci.org/medic/medic-android.svg"/></a>

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
Expand Down Expand Up @@ -64,7 +73,7 @@ To add a new brand:

1. add `productFlavors { <new_brand> { ... } }` in `build.gradle`
1. add icons, strings etc. in `src/<new_brand>`
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

Expand Down
18 changes: 12 additions & 6 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -56,7 +56,7 @@ def getVersionCode = {
}

def getVersionName = {
System.env.TRAVIS_TAG ?: 'SNAPSHOT'
System.env.RELEASE_VERSION ?: 'SNAPSHOT'
}

android {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
Expand Down
2 changes: 1 addition & 1 deletion fastlane/Fastfile
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
android:screenOrientation="portrait"/>
<activity android:name="SettingsDialogActivity"
android:screenOrientation="portrait"/>
<activity android:name="RequestPermissionActivity"
android:screenOrientation="portrait"/>
<activity android:name="AppUrlIntentActivity" android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/medicmobile/webapp/mobile/MrdtSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}
}
21 changes: 20 additions & 1 deletion src/main/java/org/medicmobile/webapp/mobile/SettingsStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Binary file added src/main/res/drawable-hdpi/location_pin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/main/res/drawable-mdpi/location_pin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/main/res/drawable-xhdpi/location_pin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/main/res/drawable-xxhdpi/location_pin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 77 additions & 0 deletions src/main/res/layout/request_permission.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp"
android:paddingRight="16dp">

<ImageView
android:id="@+id/locIcon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="80dp"
android:contentDescription="@string/locRequestIconDescription"
android:scaleType="center"
android:src="@drawable/location_pin" />

<TextView
android:id="@+id/locTitleText"
style="@android:style/Widget.DeviceDefault.Light.TextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/locIcon"
android:layout_marginTop="8dp"
android:gravity="center"
android:padding="10dp"
android:text="@string/locRequestTitle"
android:textSize="18sp"
android:textStyle="bold" />

<TextView
android:id="@+id/locMessageText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/locTitleText"
android:layout_marginTop="10dp"
android:gravity="center"
android:padding="20dp"
android:text="@string/locRequestMessage"
android:textSize="18sp" />

<LinearLayout
android:id="@+id/locButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingBottom="20dp">

<Button
android:id="@+id/locNegativeButton"
style="@style/Widget.AppCompat.Button.Borderless.Blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClickNegative"
android:text="@string/locRequestDenyButton"
tools:ignore="OnClick" />

<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />

<Button
android:id="@+id/locOkButton"
style="@style/Widget.AppCompat.Button.Borderless.Blue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClickOk"
android:text="@string/locRequestOkButton"
tools:ignore="OnClick" />
</LinearLayout>

</RelativeLayout>
8 changes: 8 additions & 0 deletions src/main/res/values-bm/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="locRequestTitle">I bɛ yɔrɔ min na</string>
<string name="locRequestMessage">%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ɛ.</string>
<string name="locRequestOkButton">Awɔ</string>
<string name="locRequestDenyButton">Ayi man sɔn</string>
<string name="locRequestIconDescription">Icône de mon emplacement</string> <!-- leave it in fr, not relevant -->
</resources>
8 changes: 8 additions & 0 deletions src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="locRequestTitle">Acceso a la ubicación</string>
<string name="locRequestMessage">%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.</string>
<string name="locRequestOkButton">Activar</string>
<string name="locRequestDenyButton">No gracias</string>
<string name="locRequestIconDescription">Ícono de mi ubicación</string>
</resources>
8 changes: 8 additions & 0 deletions src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="locRequestTitle">Accès à la position</string>
<string name="locRequestMessage">%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.</string>
<string name="locRequestOkButton">Activer</string>
<string name="locRequestDenyButton">Non merci</string>
<string name="locRequestIconDescription">Icône de mon emplacement</string>
</resources>
8 changes: 8 additions & 0 deletions src/main/res/values-hi/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="locRequestTitle">स्थान का उपयोग</string>
<string name="locRequestMessage">यह ऐप आपके क्षेत्र में स्वास्थ्य परिणामों का विश्लेषण और सुधार करने के लिए स्थान डेटा एकत्र करता है। स्थान पहुंच की अनुमति देने के लिए "चालू करें" चुनें, और फिर अगले संकेत की पुष्टि करें।</string>
<string name="locRequestOkButton">चालू करो</string>
<string name="locRequestDenyButton">जी नहीं, धन्यवाद</string>
<string name="locRequestIconDescription">मेरा स्थान आइकन</string>
</resources>
8 changes: 8 additions & 0 deletions src/main/res/values-in/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="locRequestTitle">Akses lokasi</string>
<string name="locRequestMessage">%s mengumpulkan data lokasi untuk menganalisis dan meningkatkan hasil kesehatan di wilayah Anda. Untuk mengizinkan akses lokasi, pilih \"Nyalakan\", lalu konfirmasikan permintaan berikutnya.</string>
<string name="locRequestOkButton">Nyalakan</string>
<string name="locRequestDenyButton">Tidak, terima kasih</string>
<string name="locRequestIconDescription">Ikon lokasiku</string>
</resources>
8 changes: 8 additions & 0 deletions src/main/res/values-ne/strings.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="locRequestTitle">स्थान पहुँच</string>
<string name="locRequestMessage">यो अनुप्रयोगले विश्लेषण गर्दछ र तपाईंको क्षेत्रमा स्वास्थ्य परिणामहरू सुधार गर्न स्थान डाटा संकलन गर्दछ। स्थान पहुँचलाई अनुमति दिन "खोल्नुहोस्" चयन गर्नुहोस्, र त्यसपछि अर्को प्रॉम्प्ट पुष्टि गर्नुहोस्।</string>
<string name="locRequestOkButton">खोल्नुहोस्</string>
<string name="locRequestDenyButton">धन्यबाद</string>
<string name="locRequestIconDescription">मेरो स्थान आइकन</string>
</resources>
6 changes: 6 additions & 0 deletions src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,10 @@
<string name="promptChooseImage">Choose image</string>

<string name="spnCompressingImage">Compressing image…</string>

<string name="locRequestTitle">Location access</string>
<string name="locRequestMessage">%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.</string>
<string name="locRequestOkButton">Turn on</string>
<string name="locRequestDenyButton">No thanks</string>
<string name="locRequestIconDescription">My location icon</string>
</resources>
4 changes: 4 additions & 0 deletions src/main/res/values/styles.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@
<item name="android:paddingLeft">32dp</item>
<item name="android:paddingRight">32dp</item>
</style>
<style name="Widget.AppCompat.Button.Borderless.Blue" parent="Widget.AppCompat.Button.Borderless">
<item name="android:textColor">#0073cd</item>
<item name="android:textAllCaps">false</item>
</style>
</resources>
Loading

0 comments on commit a4c31ab

Please sign in to comment.