diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index daddf43b..1712ba7e 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -43,8 +43,6 @@ android:configChanges="orientation|screenSize"/> - diff --git a/src/main/java/medic/android/PromptForPermissionsActivity.java b/src/main/java/medic/android/PromptForPermissionsActivity.java deleted file mode 100644 index 16acc39b..00000000 --- a/src/main/java/medic/android/PromptForPermissionsActivity.java +++ /dev/null @@ -1,166 +0,0 @@ -package medic.android; - -import android.app.Activity; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; -import android.text.Html; -import android.view.View; -import android.widget.TextView; - -import org.medicmobile.webapp.mobile.R; - -import static android.content.pm.PackageManager.PERMISSION_GRANTED; -import static android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS; -import static org.medicmobile.webapp.mobile.MedicLog.trace; - -/** - * To support Android 6.0+ (marshmallow), we must request SMS permissions at - * runtime as well as in {@code AndroidManifest.xml}. - * @see https://developer.android.com/intl/ru/about/versions/marshmallow/android-6.0-changes.html#behavior-runtime-permissions - * - * TODO this class was copy/pasted from medic-gateway. It should be pulled into - * a separate lib so that both projects can share the same source. - */ -public abstract class PromptForPermissionsActivity extends Activity implements ActivityCompat.OnRequestPermissionsResultCallback { - private static final String X_IS_DEMAND = "isDemand"; - private static final String X_PERMISSIONS_TYPE = "permissionsType"; - - private boolean isDemand; - private boolean deniedBefore; - private int permissionsRequestType; - -//> API - protected abstract boolean refuseToFunctionWithoutPermissions(); - protected abstract Object[][] getPermissionRequests(); - protected abstract Class getNextActivityClass(); - - @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - isDemand = getIntent().getBooleanExtra(X_IS_DEMAND, false); - - trace(this, "onCreate() :: isDemand=%s, permissionsRequestType=%s", isDemand, permissionsRequestType); - - setContentView(R.layout.prompt_for_permissions); - - int promptTextId; - if(isDemand) { - promptTextId = R.string.txtDemandPermissions; - } else { - permissionsRequestType = getIntent().getIntExtra(X_PERMISSIONS_TYPE, 0); - promptTextId = (int) getPermissionRequests()[permissionsRequestType][0]; - makePermissionRequest(); - } - - String appName = getResources().getString(R.string.app_name); - CharSequence text = Html.fromHtml(getResources().getString(promptTextId, appName)); - ((TextView) findViewById(R.id.txtPermissionsPrompt)).setText(text); - } - -//> EVENT HANDLERS - public void btnOk_onClick(View v) { - if(isDemand) { - // open app manager for this app - Intent i = new Intent(ACTION_APPLICATION_DETAILS_SETTINGS, - Uri.fromParts("package", getPackageName(), null)); - i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(i); - - finish(); - } else makePermissionRequest(); - } - - @SuppressWarnings("PMD.UseVarargs") - public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { - boolean allGranted = true; - for(int res : grantResults) allGranted &= res == PERMISSION_GRANTED; - - if(allGranted) { - nextActivity(this, permissionsRequestType + 1); - } else if(refuseToFunctionWithoutPermissions()) { - // For some flavours, we don't want to give people the option to use the app without the - // correct permissions. If the permission is not granted, re-request the same. - if(canShowPromptFor(this, permissionsRequestType)) { // NOPMD - // Don't do anything - the user can re-read the on-screen advice. - } else { - // The user has checked the "don't ask me again"/"never allow" box (TODO which one?), so we have to step things up. - startActivity(demandPermissions(this)); - finish(); - } - } else { - if(!deniedBefore && canShowPromptFor(this, permissionsRequestType)) { - // Allow user to read the advice on the screen - deniedBefore = true; - } else nextActivity(this, permissionsRequestType + 1); - } - } - - protected void startPermissionsRequestChain(Activity a) { - nextActivity(a, 0); - } - - private void nextActivity(Activity a, int firstPermissionToConsider) { - trace(a, "nextActivity() :: %s", firstPermissionToConsider); - - Intent next = null; - - for(int p=firstPermissionToConsider; p PRIVATE HELPERS - private void makePermissionRequest() { - ActivityCompat.requestPermissions(this, getPermissions(permissionsRequestType), 0); - } - - private Intent requestPermission(Activity a, int permissionsRequestType) { - trace(a, "requestPermission() :: p=%s", permissionsRequestType); - Intent i = new Intent(a, getClass()); - i.putExtra(X_PERMISSIONS_TYPE, permissionsRequestType); - i.putExtra(X_IS_DEMAND, false); - return i; - } - - private Intent demandPermissions(Activity a) { - trace(a, "demandPermission()"); - Intent i = new Intent(a, getClass()); - i.putExtra(X_IS_DEMAND, true); - return i; - } -} diff --git a/src/main/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java b/src/main/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java index dcbd725c..7a1a73b7 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java +++ b/src/main/java/org/medicmobile/webapp/mobile/EmbeddedBrowserActivity.java @@ -1,16 +1,14 @@ package org.medicmobile.webapp.mobile; -import android.Manifest.permission; +import android.Manifest; import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; import android.app.ActivityManager; import android.net.Uri; import android.net.ConnectivityManager; import android.os.Bundle; +import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.view.KeyEvent; import android.view.Menu; @@ -19,6 +17,7 @@ import android.view.Window; import android.webkit.ValueCallback; import android.widget.Toast; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import java.io.ByteArrayInputStream; import java.util.Collections; @@ -32,7 +31,6 @@ import org.xwalk.core.XWalkWebResourceRequest; import org.xwalk.core.XWalkWebResourceResponse; -import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static java.lang.Boolean.parseBoolean; import static org.medicmobile.webapp.mobile.BuildConfig.DEBUG; import static org.medicmobile.webapp.mobile.BuildConfig.DISABLE_APP_URL_VALIDATION; @@ -51,8 +49,7 @@ public class EmbeddedBrowserActivity extends LockableActivity { static final int GRAB_PHOTO = (0 << 3) | NON_SIMPRINTS_FLAGS; static final int GRAB_MRDT_PHOTO = (1 << 3) | NON_SIMPRINTS_FLAGS; - private static final long FIVE_MINS = 5 * 60 * 1000; - private static final float ANY_DISTANCE = 0f; + private final static int ACCESS_FINE_LOCATION_PERMISSION_REQUEST = (int)Math.random(); private static final ValueCallback IGNORE_RESULT = new ValueCallback() { public void onReceiveValue(String result) { /* ignore */ } @@ -108,7 +105,6 @@ public void onReceiveValue(String result) { configureUseragent(); - enableLocationUpdates(); setUpUiClient(container); enableRemoteChromeDebugging(); enableJavascript(container); @@ -326,6 +322,25 @@ public void onGeolocationPermissionsShowPrompt( }); } + public boolean getLocationPermissions() { + if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PERMISSION_GRANTED) { + return true; + } + + String[] permissions = { Manifest.permission.ACCESS_FINE_LOCATION }; + ActivityCompat.requestPermissions(this, permissions, ACCESS_FINE_LOCATION_PERMISSION_REQUEST); + return false; + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode != ACCESS_FINE_LOCATION_PERMISSION_REQUEST) { + return; + } + String javaScript = "angular.element(document.body).injector().get('AndroidApi').v1.locationPermissionRequestResolved();"; + evaluateJavascript(String.format(javaScript)); + } + @SuppressLint("SetJavaScriptEnabled") private void enableJavascript(XWalkView container) { container.getSettings().setJavaScriptEnabled(true); @@ -333,8 +348,6 @@ private void enableJavascript(XWalkView container) { MedicAndroidJavascript maj = new MedicAndroidJavascript(this); maj.setAlert(new Alert(this)); - maj.setLocationManager((LocationManager) this.getSystemService(Context.LOCATION_SERVICE)); - maj.setActivityManager((ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE)); maj.setConnectivityManager((ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE)); @@ -342,58 +355,6 @@ private void enableJavascript(XWalkView container) { container.addJavascriptInterface(maj, "medicmobile_android"); } - /** - * Make the app poll the location providers periodically. This should mean that calls - * to MedicAndroidJavascript.getLocation() are reasonably up-to-date, and an initial - * location is likely to have been resolved by the time the user first fills a form and - * getLocation() is called. However, longer-term we are likely to want a more explicit - * async getLocation() implementation which is triggered only when needed. - * @see https://github.com/medic/medic-projects/issues/2629 - * @deprecated @see https://github.com/medic/medic-webapp/issues/3781 - */ - @Deprecated - private void enableLocationUpdates() { - if(ContextCompat.checkSelfPermission(this, permission.ACCESS_FINE_LOCATION) != PERMISSION_GRANTED) { - log("EmbeddedBrowserActivity.enableLocationUpdates() :: Cannot enable location updates: permission ACCESS_FINE_LOCATION not granted."); - return; - } - - LocationManager m = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE); - - if(m == null) { - log("EmbeddedBrowserActivity.enableLocationUpdates() :: Cannot enable location updates: LOCATION_SERVICE could not be fetched."); - return; - } - - requestLocationUpdates(m, LocationManager.GPS_PROVIDER); - requestLocationUpdates(m, LocationManager.NETWORK_PROVIDER); - } - - private void requestLocationUpdates(LocationManager m, String locationProvider) { - try { - if(m.isProviderEnabled(locationProvider)) { - // Method bodies are empty because we need the location updates to be running constantly so - // that recent location can be requested when required from Javascript. - m.requestLocationUpdates(locationProvider, FIVE_MINS, ANY_DISTANCE, new LocationListener() { - @SuppressWarnings("PMD.UncommentedEmptyMethodBody") - public void onLocationChanged(Location location) {} - @SuppressWarnings("PMD.UncommentedEmptyMethodBody") - public void onProviderDisabled(String provider) {} - @SuppressWarnings("PMD.UncommentedEmptyMethodBody") - public void onProviderEnabled(String provider) {} - @SuppressWarnings("PMD.UncommentedEmptyMethodBody") - public void onStatusChanged(String provider, int status, Bundle extras) {} - }); - } else { - log("EmbeddedBrowserActivity.requestLocationUpdates(%s) :: Cannot get updates: not enabled or phone does not have this feature.", - locationProvider); - } - } catch(SecurityException ex) { - log(ex, "EmbeddedBrowserActivity.requestLocationUpdates(%s) :: Exception thrown while checking provider.", - locationProvider); - } - } - private void enableStorage(XWalkView container) { XWalkSettings settings = container.getSettings(); diff --git a/src/main/java/org/medicmobile/webapp/mobile/MedicAndroidJavascript.java b/src/main/java/org/medicmobile/webapp/mobile/MedicAndroidJavascript.java index 3b7d5abd..3dc6e1f3 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/MedicAndroidJavascript.java +++ b/src/main/java/org/medicmobile/webapp/mobile/MedicAndroidJavascript.java @@ -1,12 +1,8 @@ package org.medicmobile.webapp.mobile; -import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.ActivityManager.MemoryInfo; import android.app.DatePickerDialog; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationManager; import android.net.ConnectivityManager; import android.net.NetworkCapabilities; import android.net.NetworkInfo; @@ -47,7 +43,6 @@ public class MedicAndroidJavascript { private final MrdtSupport mrdt; private final SmsSender smsSender; - private LocationManager locationManager; private ActivityManager activityManager; private ConnectivityManager connectivityManager; private Alert soundAlert; @@ -63,10 +58,6 @@ public void setAlert(Alert soundAlert) { this.soundAlert = soundAlert; } - public void setLocationManager(LocationManager locationManager) { - this.locationManager = locationManager; - } - public void setActivityManager(ActivityManager activityManager) { this.activityManager = activityManager; } @@ -122,32 +113,10 @@ private JSONObject getDataUsage(long rx, long tx) throws JSONException { .put("tx", tx); } - @Deprecated @org.xwalk.core.JavascriptInterface @android.webkit.JavascriptInterface - @SuppressLint("MissingPermission") // handled by catch(Exception) - /** - * @deprecated Location should be fetched directly from the browser. - * @see https://github.com/medic/medic-webapp/issues/3781 - */ - public String getLocation() { - try { - if(locationManager == null) return jsonError("LocationManager not set. Cannot retrieve location."); - - String provider = locationManager.getBestProvider(new Criteria(), true); - if(provider == null) return jsonError("No location provider available."); - - Location loc = locationManager.getLastKnownLocation(provider); - - if(loc == null) return jsonError("Provider '" + provider + "' did not provide a location."); - - return new JSONObject() - .put("lat", loc.getLatitude()) - .put("long", loc.getLongitude()) - .toString(); - } catch(Exception ex) { - return jsonError("Problem fetching location: ", ex); - } + public boolean getLocationPermissions() { + return this.parent.getLocationPermissions(); } @org.xwalk.core.JavascriptInterface diff --git a/src/main/java/org/medicmobile/webapp/mobile/MmPromptForPermissionsActivity.java b/src/main/java/org/medicmobile/webapp/mobile/MmPromptForPermissionsActivity.java deleted file mode 100644 index 5dcd1ed7..00000000 --- a/src/main/java/org/medicmobile/webapp/mobile/MmPromptForPermissionsActivity.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.medicmobile.webapp.mobile; - -import android.Manifest.permission; -import android.app.Activity; - -import medic.android.PromptForPermissionsActivity; - -public class MmPromptForPermissionsActivity extends PromptForPermissionsActivity { - private static final Object[][] PERMISSIONS_REQUESTS = { - /* location */ { R.string.txtPermissionsPrompt_location, new String[] { permission.ACCESS_COARSE_LOCATION, permission.ACCESS_FINE_LOCATION } }, - }; - - @Override protected boolean refuseToFunctionWithoutPermissions() { return true; } - - @Override protected Object[][] getPermissionRequests() { return PERMISSIONS_REQUESTS; } - - @Override protected Class getNextActivityClass() { return EmbeddedBrowserActivity.class; } - - public static void startPermissionsRequestChainFrom(Activity from) { - new MmPromptForPermissionsActivity().startPermissionsRequestChain(from); - } -} diff --git a/src/main/java/org/medicmobile/webapp/mobile/SettingsDialogActivity.java b/src/main/java/org/medicmobile/webapp/mobile/SettingsDialogActivity.java index ac72f419..affd41f2 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/SettingsDialogActivity.java +++ b/src/main/java/org/medicmobile/webapp/mobile/SettingsDialogActivity.java @@ -139,7 +139,7 @@ private void backToWebview() { private void saveSettings(WebappSettings s) { try { settings.updateWith(s); - MmPromptForPermissionsActivity.startPermissionsRequestChainFrom(this); + this.backToWebview(); } catch(IllegalSettingsException ex) { if(DEBUG) trace(ex, "Tried to save illegal setting."); for(IllegalSetting error : ex.errors) { diff --git a/src/main/java/org/medicmobile/webapp/mobile/Utils.java b/src/main/java/org/medicmobile/webapp/mobile/Utils.java index 7447e7e6..7b62451f 100644 --- a/src/main/java/org/medicmobile/webapp/mobile/Utils.java +++ b/src/main/java/org/medicmobile/webapp/mobile/Utils.java @@ -37,11 +37,11 @@ static boolean intentHandlerAvailableFor(Context ctx, Intent intent) { static void startAppActivityChain(Activity a) { if(SettingsStore.in(a).hasWebappSettings()) { - MmPromptForPermissionsActivity.startPermissionsRequestChainFrom(a); + a.startActivity(new Intent(a, EmbeddedBrowserActivity.class)); } else { a.startActivity(new Intent(a, SettingsDialogActivity.class)); - a.finish(); } + a.finish(); } public static ProgressDialog showSpinner(Context ctx, int messageId) { diff --git a/src/main/res/layout/prompt_for_permissions.xml b/src/main/res/layout/prompt_for_permissions.xml deleted file mode 100644 index 2da179cf..00000000 --- a/src/main/res/layout/prompt_for_permissions.xml +++ /dev/null @@ -1,35 +0,0 @@ - - - - - -