diff --git a/config.xml b/config.xml
index 07ec852ae..8059c0c81 100644
--- a/config.xml
+++ b/config.xml
@@ -80,18 +80,9 @@
-
- We need your location so you can attach it as part of your submissions.
-
-
- We need your location so you can attach it as part of your submissions.
-
We need camera access to take pictures so you can attach them as part of your submissions.
-
- We need microphone access to record sounds so you can attach them as part of your submissions.
-
We need photo library access to get pictures from there so you can attach them as part of your submissions.
diff --git a/cordova-plugin-moodleapp/plugin.xml b/cordova-plugin-moodleapp/plugin.xml
index f25baf041..d8e5e6abc 100644
--- a/cordova-plugin-moodleapp/plugin.xml
+++ b/cordova-plugin-moodleapp/plugin.xml
@@ -15,9 +15,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -27,5 +48,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ We need your location so you can attach it as part of your submissions.
+
+
+
+ We need your location so you can attach it as part of your submissions.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ We need microphone access to record sounds so you can attach them as part of your submissions.
+
+
+
+
+
+
diff --git a/cordova-plugin-moodleapp/src/android/Diagnostic.java b/cordova-plugin-moodleapp/src/android/Diagnostic.java
new file mode 100644
index 000000000..77b97745c
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/android/Diagnostic.java
@@ -0,0 +1,1073 @@
+// (C) Copyright 2015 Moodle Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.moodle.moodlemobile;
+
+/*
+ * Imports
+ */
+import static android.content.Context.BATTERY_SERVICE;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStreamReader;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaInterface;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.net.ConnectivityManager;
+import android.net.Uri;
+import android.os.BatteryManager;
+import android.os.Build;
+import android.util.Log;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+
+
+import androidx.core.app.ActivityCompat;
+
+/**
+ * Diagnostic plugin implementation for Android
+ */
+public class Diagnostic extends CordovaPlugin{
+
+
+ /*************
+ * Constants *
+ *************/
+
+ /**
+ * Tag for debug log messages
+ */
+ public static final String TAG = "Diagnostic";
+
+
+ /**
+ * Map of "dangerous" permissions that need to be requested at run-time (Android 6.0/API 23 and above)
+ * See http://developer.android.com/guide/topics/security/permissions.html#perm-groups
+ */
+ protected static final Map permissionsMap;
+ static {
+ Map _permissionsMap = new HashMap ();
+
+ // API 1-22+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACCESS_COARSE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACCESS_FINE_LOCATION", "android.permission.ACCESS_FINE_LOCATION");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ADD_VOICEMAIL", "android.permission.ADD_VOICEMAIL");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BODY_SENSORS", "android.permission.BODY_SENSORS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "CALL_PHONE", "android.permission.CALL_PHONE");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "CAMERA", "android.permission.CAMERA");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "GET_ACCOUNTS", "android.permission.GET_ACCOUNTS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "PROCESS_OUTGOING_CALLS", "android.permission.PROCESS_OUTGOING_CALLS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_CALENDAR", "android.permission.READ_CALENDAR");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_CALL_LOG", "android.permission.READ_CALL_LOG");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_CONTACTS", "android.permission.READ_CONTACTS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_EXTERNAL_STORAGE", "android.permission.READ_EXTERNAL_STORAGE");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_PHONE_STATE", "android.permission.READ_PHONE_STATE");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_SMS", "android.permission.READ_SMS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "RECEIVE_MMS", "android.permission.RECEIVE_MMS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "RECEIVE_SMS", "android.permission.RECEIVE_SMS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "RECEIVE_WAP_PUSH", "android.permission.RECEIVE_WAP_PUSH");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "RECORD_AUDIO", "android.permission.RECORD_AUDIO");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "SEND_SMS", "android.permission.SEND_SMS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "USE_SIP", "android.permission.USE_SIP");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "WRITE_CALENDAR", "android.permission.WRITE_CALENDAR");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "WRITE_CALL_LOG", "android.permission.WRITE_CALL_LOG");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "WRITE_CONTACTS", "android.permission.WRITE_CONTACTS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "WRITE_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE");
+
+ // API 26+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ANSWER_PHONE_CALLS", "android.permission.ANSWER_PHONE_CALLS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_PHONE_NUMBERS", "android.permission.READ_PHONE_NUMBERS");
+
+ // API 28+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACCEPT_HANDOVER", "android.permission.ACCEPT_HANDOVER");
+
+ // API 29+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_BACKGROUND_LOCATION");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACCESS_MEDIA_LOCATION", "android.permission.ACCESS_MEDIA_LOCATION");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACTIVITY_RECOGNITION", "android.permission.ACTIVITY_RECOGNITION");
+
+ // API 31+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BLUETOOTH_ADVERTISE", "android.permission.BLUETOOTH_ADVERTISE");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BLUETOOTH_CONNECT", "android.permission.BLUETOOTH_CONNECT");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BLUETOOTH_SCAN", "android.permission.BLUETOOTH_SCAN");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "UWB_RANGING", "android.permission.UWB_RANGING");
+
+ // API 33+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BODY_SENSORS_BACKGROUND", "android.permission.BODY_SENSORS_BACKGROUND");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "NEARBY_WIFI_DEVICES", "android.permission.NEARBY_WIFI_DEVICES");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "POST_NOTIFICATIONS", "android.permission.POST_NOTIFICATIONS");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_MEDIA_AUDIO", "android.permission.READ_MEDIA_AUDIO");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_MEDIA_IMAGES", "android.permission.READ_MEDIA_IMAGES");
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_MEDIA_VIDEO", "android.permission.READ_MEDIA_VIDEO");
+
+ permissionsMap = Collections.unmodifiableMap(_permissionsMap);
+ }
+
+ /**
+ * Map of minimum build SDK version supported by defined permissions
+ */
+ protected static final Map minSdkPermissionMap;
+ static {
+ Map _permissionsMap = new HashMap ();
+
+ // API 26+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ANSWER_PHONE_CALLS", 26);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_PHONE_NUMBERS", 26);
+
+ // API 28+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACCEPT_HANDOVER", 28);
+
+ // API 29+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACCESS_BACKGROUND_LOCATION", 29);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACCESS_MEDIA_LOCATION", 29);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "ACTIVITY_RECOGNITION", 29);
+
+ // API 31+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BLUETOOTH_ADVERTISE", 31);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BLUETOOTH_CONNECT", 31);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BLUETOOTH_SCAN", 31);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "UWB_RANGING", 31);
+
+ // API 33+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "BODY_SENSORS_BACKGROUND", 33);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "NEARBY_WIFI_DEVICES", 33);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "POST_NOTIFICATIONS", 33);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_MEDIA_AUDIO", 33);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_MEDIA_IMAGES", 33);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_MEDIA_VIDEO", 33);
+
+ minSdkPermissionMap = Collections.unmodifiableMap(_permissionsMap);
+ }
+
+ /**
+ * Map of maximum build SDK version supported by defined permissions
+ */
+ protected static final Map maxSdkPermissionMap;
+ static {
+ Map _permissionsMap = new HashMap ();
+
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "READ_EXTERNAL_STORAGE", 32);
+ Diagnostic.addBiDirMapEntry(_permissionsMap, "WRITE_EXTERNAL_STORAGE", 29);
+
+ maxSdkPermissionMap = Collections.unmodifiableMap(_permissionsMap);
+ }
+
+
+ /*
+ * Map of permission request code to callback context
+ */
+ protected HashMap callbackContexts = new HashMap();
+
+ /*
+ * Map of permission request code to permission statuses
+ */
+ protected HashMap permissionStatuses = new HashMap();
+
+
+ /**
+ * User authorised permission
+ */
+ protected static final String STATUS_GRANTED = "GRANTED";
+
+ /**
+ * User denied permission (without checking "never ask again")
+ */
+ protected static final String STATUS_DENIED_ONCE = "DENIED_ONCE";
+
+ /**
+ * User denied permission and checked "never ask again"
+ */
+ protected static final String STATUS_DENIED_ALWAYS = "DENIED_ALWAYS";
+
+ /**
+ * Authorisation has not yet been requested for permission
+ */
+ protected static final String STATUS_NOT_REQUESTED = "NOT_REQUESTED";
+
+ public static final String CPU_ARCH_UNKNOWN = "unknown";
+ public static final String CPU_ARCH_ARMv6 = "ARMv6";
+ public static final String CPU_ARCH_ARMv7 = "ARMv7";
+ public static final String CPU_ARCH_ARMv8 = "ARMv8";
+ public static final String CPU_ARCH_X86 = "X86";
+ public static final String CPU_ARCH_X86_64 = "X86_64";
+ public static final String CPU_ARCH_MIPS = "MIPS";
+ public static final String CPU_ARCH_MIPS_64 = "MIPS_64";
+
+ protected static final String externalStorageClassName = "cordova.plugins.Diagnostic_External_Storage";
+ protected static final Integer GET_EXTERNAL_SD_CARD_DETAILS_PERMISSION_REQUEST = 1000;
+
+ /*************
+ * Variables *
+ *************/
+
+ /**
+ * Singleton class instance
+ */
+ public static Diagnostic instance = null;
+
+ boolean debugEnabled = false;
+
+
+ /**
+ * Current Cordova callback context (on this thread)
+ */
+ protected CallbackContext currentContext;
+
+ protected Context applicationContext;
+
+ protected SharedPreferences sharedPref;
+ protected SharedPreferences.Editor editor;
+
+ /*************
+ * Public API
+ ************/
+
+ /**
+ * Constructor.
+ */
+ public Diagnostic() {}
+
+ public static Diagnostic getInstance(){
+ return instance;
+ }
+
+ /**
+ * Sets the context of the Command. This can then be used to do things like
+ * get file paths associated with the Activity.
+ *
+ * @param cordova The context of the main Activity.
+ * @param webView The CordovaWebView Cordova is running in.
+ */
+ public void initialize(CordovaInterface cordova, CordovaWebView webView) {
+ Log.d(TAG, "initialize()");
+ instance = this;
+
+ applicationContext = this.cordova.getActivity().getApplicationContext();
+ sharedPref = cordova.getActivity().getSharedPreferences(TAG, Activity.MODE_PRIVATE);
+ editor = sharedPref.edit();
+
+ super.initialize(cordova, webView);
+ }
+
+ /**
+ * Executes the request and returns PluginResult.
+ *
+ * @param action The action to execute.
+ * @param args JSONArry of arguments for the plugin.
+ * @param callbackContext The callback id used when calling back into JavaScript.
+ * @return True if the action was valid, false if not.
+ */
+ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+ currentContext = callbackContext;
+
+ try {
+ if (action.equals("enableDebug")){
+ debugEnabled = true;
+ logDebug("Debug enabled");
+ callbackContext.success();
+ } else if (action.equals("switchToSettings")){
+ switchToAppSettings();
+ callbackContext.success();
+ } else if (action.equals("switchToMobileDataSettings")){
+ switchToMobileDataSettings();
+ callbackContext.success();
+ } else if (action.equals("switchToWirelessSettings")){
+ switchToWirelessSettings();
+ callbackContext.success();
+ } else if(action.equals("isDataRoamingEnabled")) {
+ if(Build.VERSION.SDK_INT <= 32) { // Android 12L
+ callbackContext.success(isDataRoamingEnabled() ? 1 : 0);
+ } else {
+ callbackContext.error("Data roaming setting not available on Android 12L / API32+");
+ }
+ callbackContext.success(isDataRoamingEnabled() ? 1 : 0);
+ } else if(action.equals("getPermissionAuthorizationStatus")) {
+ this.getPermissionAuthorizationStatus(args);
+ } else if(action.equals("getPermissionsAuthorizationStatus")) {
+ this.getPermissionsAuthorizationStatus(args);
+ } else if(action.equals("requestRuntimePermission")) {
+ this.requestRuntimePermission(args);
+ } else if(action.equals("requestRuntimePermissions")) {
+ this.requestRuntimePermissions(args);
+ } else if(action.equals("requestMicrophoneAuthorization")) {
+ this.requestRuntimePermission("RECORD_AUDIO");
+ } else if(action.equals("isADBModeEnabled")) {
+ callbackContext.success(isADBModeEnabled() ? 1 : 0);
+ } else if(action.equals("isDeviceRooted")) {
+ callbackContext.success(isDeviceRooted() ? 1 : 0);
+ } else if(action.equals("isMobileDataEnabled")) {
+ callbackContext.success(isMobileDataEnabled() ? 1 : 0);
+ } else if(action.equals("restart")) {
+ this.restart(args);
+ } else if(action.equals("getArchitecture")) {
+ callbackContext.success(getCPUArchitecture());
+ } else if(action.equals("getCurrentBatteryLevel")) {
+ callbackContext.success(getCurrentBatteryLevel());
+ } else if(action.equals("isAirplaneModeEnabled")) {
+ callbackContext.success(isAirplaneModeEnabled() ? 1 : 0);
+ } else if(action.equals("getDeviceOSVersion")) {
+ callbackContext.success(getDeviceOSVersion());
+ } else if(action.equals("getBuildOSVersion")) {
+ callbackContext.success(getBuildOSVersion());
+ } else {
+ handleError("Invalid action");
+ return false;
+ }
+ }catch(Exception e ) {
+ handleError("Exception occurred: ".concat(e.getMessage()));
+ return false;
+ }
+ return true;
+ }
+
+ public void restart(JSONArray args) throws Exception{
+ boolean cold = args.getBoolean(0);
+ if(cold){
+ doColdRestart();
+ }else{
+ doWarmRestart();
+ }
+ }
+
+
+ public boolean isDataRoamingEnabled() throws Exception {
+ return Settings.Global.getInt(this.cordova.getActivity().getContentResolver(), Settings.Global.DATA_ROAMING, 0) == 1;
+ }
+
+ public void switchToAppSettings() {
+ logDebug("Switch to App Settings");
+ Intent appIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts("package", cordova.getActivity().getPackageName(), null);
+ appIntent.setData(uri);
+ cordova.getActivity().startActivity(appIntent);
+ }
+
+
+ public void switchToMobileDataSettings() {
+ logDebug("Switch to Mobile Data Settings");
+ Intent settingsIntent = new Intent(Settings.ACTION_DATA_ROAMING_SETTINGS);
+ cordova.getActivity().startActivity(settingsIntent);
+ }
+
+ public void switchToWirelessSettings() {
+ logDebug("Switch to wireless Settings");
+ Intent settingsIntent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
+ cordova.getActivity().startActivity(settingsIntent);
+ }
+
+ public void getPermissionsAuthorizationStatus(JSONArray args) throws Exception{
+ JSONArray permissions = args.getJSONArray(0);
+ JSONObject statuses = _getPermissionsAuthorizationStatus(jsonArrayToStringArray(permissions));
+ currentContext.success(statuses);
+ }
+
+ public void getPermissionAuthorizationStatus(JSONArray args) throws Exception{
+ String permission = args.getString(0);
+ JSONArray permissions = new JSONArray();
+ permissions.put(permission);
+ JSONObject statuses = _getPermissionsAuthorizationStatus(jsonArrayToStringArray(permissions));
+ currentContext.success(statuses.getString(permission));
+ }
+
+ public void requestRuntimePermissions(JSONArray args) throws Exception{
+ JSONArray permissions = args.getJSONArray(0);
+ int requestId = storeCurrentContextByRequestId();
+ _requestRuntimePermissions(permissions, requestId);
+ }
+
+ public void requestRuntimePermission(JSONArray args) throws Exception{
+ requestRuntimePermission(args.getString(0));
+ }
+
+ public void requestRuntimePermission(String permission) throws Exception{
+ requestRuntimePermission(permission, storeCurrentContextByRequestId());
+ }
+
+ public void requestRuntimePermission(String permission, int requestId) throws Exception{
+ JSONArray permissions = new JSONArray();
+ permissions.put(permission);
+ _requestRuntimePermissions(permissions, requestId);
+ }
+
+ /**
+ * get device ADB mode info
+ */
+ public int getADBMode(){
+ int mode;
+ if (Build.VERSION.SDK_INT >= 17){ // Jelly_Bean_MR1 and above
+ mode = Settings.Global.getInt(applicationContext.getContentResolver(), Settings.Global.ADB_ENABLED, 0);
+ } else { // Pre-Jelly_Bean_MR1
+ mode = Settings.Secure.getInt(applicationContext.getContentResolver(), Settings.Secure.ADB_ENABLED, 0);
+ }
+ return mode;
+ }
+
+ /**
+ * checks if ADB mode is on
+ * especially for debug mode check
+ */
+ public boolean isADBModeEnabled(){
+ boolean result = false;
+ try {
+ result = getADBMode() == 1;
+ } catch (Exception e) {
+ logError(e.getMessage());
+ }
+ logDebug("ADB mode enabled: " + result);
+ return result;
+ }
+
+ /**
+ * checks if device is rooted
+ * refer to: https://stackoverflow.com/questions/1101380
+ */
+ public boolean isDeviceRooted(){
+ // from build info
+ String buildTags = android.os.Build.TAGS;
+ if (buildTags != null && buildTags.contains("test-keys")) {
+ return true;
+ }
+
+ // from binary exists
+ try {
+ String[] paths = { "/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su",
+ "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su" };
+ for (String path : paths) {
+ if (new File(path).exists()) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ logDebug(e.getMessage());
+ }
+
+ // from command authority
+ Process process = null;
+ try {
+ process = Runtime.getRuntime().exec(new String[] { "/system/xbin/which", "su" });
+ BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ if (in.readLine() != null) {
+ return true;
+ }
+ } catch (Exception e) {
+ logDebug(e.getMessage());
+ } finally {
+ if (process != null) process.destroy();
+ }
+
+ return false;
+ }
+
+ // https://stackoverflow.com/a/12864897/777265
+ public boolean isMobileDataEnabled(){
+ boolean mobileDataEnabled = false; // Assume disabled
+ ConnectivityManager cm = (ConnectivityManager) cordova.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ try {
+ Class cmClass = Class.forName(cm.getClass().getName());
+ Method method = cmClass.getDeclaredMethod("getMobileDataEnabled");
+ method.setAccessible(true);
+ mobileDataEnabled = (Boolean)method.invoke(cm);
+ } catch (Exception e) {
+ logDebug(e.getMessage());
+ }
+ return mobileDataEnabled;
+ }
+
+ /************
+ * Internals
+ ***********/
+
+ public void logDebug(String msg) {
+ if(msg == null) return;
+ if(debugEnabled){
+ Log.d(TAG, msg);
+ executeGlobalJavascript("console.log(\""+TAG+"[native]: "+escapeDoubleQuotes(msg)+"\")");
+ }
+ }
+
+ public void logInfo(String msg){
+ if(msg == null) return;
+ Log.i(TAG, msg);
+ if(debugEnabled){
+ executeGlobalJavascript("console.info(\""+TAG+"[native]: "+escapeDoubleQuotes(msg)+"\")");
+ }
+ }
+
+ public void logWarning(String msg){
+ if(msg == null) return;
+ Log.w(TAG, msg);
+ if(debugEnabled){
+ executeGlobalJavascript("console.warn(\""+TAG+"[native]: "+escapeDoubleQuotes(msg)+"\")");
+ }
+ }
+
+ public void logError(String msg){
+ if(msg == null) return;
+ Log.e(TAG, msg);
+ if(debugEnabled){
+ executeGlobalJavascript("console.error(\""+TAG+"[native]: "+escapeDoubleQuotes(msg)+"\")");
+ }
+ }
+
+ public String escapeDoubleQuotes(String string){
+ String escapedString = string.replace("\"", "\\\"");
+ escapedString = escapedString.replace("%22", "\\%22");
+ return escapedString;
+ }
+
+ /**
+ * Handles an error while executing a plugin API method in the specified context.
+ * Calls the registered Javascript plugin error handler callback.
+ * @param errorMsg Error message to pass to the JS error handler
+ */
+ public void handleError(String errorMsg, CallbackContext context){
+ try {
+ logError(errorMsg);
+ context.error(errorMsg);
+ } catch (Exception e) {
+ logError(e.toString());
+ }
+ }
+
+ /**
+ * Handles an error while executing a plugin API method in the current context.
+ * Calls the registered Javascript plugin error handler callback.
+ * @param errorMsg Error message to pass to the JS error handler
+ */
+ public void handleError(String errorMsg) {
+ handleError(errorMsg, currentContext);
+ }
+
+ /**
+ * Handles error during a runtime permissions request.
+ * Calls the registered Javascript plugin error handler callback
+ * then removes entries associated with the request ID.
+ * @param errorMsg Error message to pass to the JS error handler
+ * @param requestId The ID of the runtime request
+ */
+ public void handleError(String errorMsg, int requestId){
+ CallbackContext context;
+ String sRequestId = String.valueOf(requestId);
+ if (callbackContexts.containsKey(sRequestId)) {
+ context = callbackContexts.get(sRequestId);
+ }else{
+ context = currentContext;
+ }
+ handleError(errorMsg, context);
+ clearRequest(requestId);
+ }
+
+ protected JSONObject _getPermissionsAuthorizationStatus(String[] permissions) throws Exception{
+ JSONObject statuses = new JSONObject();
+ for(int i=0; i maxSdkPermissionMap.get(permission)){
+ throw new Exception("Permission "+permission+" not supported for build SDK version "+getDeviceRuntimeSdkVersion());
+ }
+
+ String androidPermission = permissionsMap.get(permission);
+ Log.d(TAG, "Requesting permission for "+androidPermission);
+ permissionsToRequest.put(androidPermission);
+ }
+ }
+ if(permissionsToRequest.length() > 0){
+ Log.v(TAG, "Requesting permissions");
+ requestPermissions(this, requestId, jsonArrayToStringArray(permissionsToRequest));
+
+ }else{
+ Log.d(TAG, "No permissions to request: returning result");
+ sendRuntimeRequestResult(requestId);
+ }
+ }
+
+ protected boolean isPermissionImplicitlyGranted(String permission) throws Exception{
+ boolean isImplicitlyGranted = false;
+ int buildTargetSdkVersion = getBuildTargetSdkVersion();
+ int deviceRuntimeSdkVersion = getDeviceRuntimeSdkVersion();
+
+ if(minSdkPermissionMap.containsKey(permission)){
+ int minSDKForPermission = minSdkPermissionMap.get(permission);
+ if(buildTargetSdkVersion >= minSDKForPermission && deviceRuntimeSdkVersion < minSDKForPermission) {
+ isImplicitlyGranted = true;
+ Log.v(TAG, "Permission "+permission+" is implicitly granted because while it's defined in build SDK version "+buildTargetSdkVersion+", the device runtime SDK version "+deviceRuntimeSdkVersion+" does not support it.");
+ }
+ }
+
+ return isImplicitlyGranted;
+ }
+
+ protected void sendRuntimeRequestResult(int requestId){
+ String sRequestId = String.valueOf(requestId);
+ CallbackContext context = callbackContexts.get(sRequestId);
+ JSONObject statuses = permissionStatuses.get(sRequestId);
+ Log.v(TAG, "Sending runtime request result for id="+sRequestId);
+ context.success(statuses);
+ }
+
+ protected int storeCurrentContextByRequestId(){
+ return storeContextByRequestId(currentContext);
+ }
+
+ protected int storeContextByRequestId(CallbackContext callbackContext){
+ String requestId = generateRandomRequestId();
+ callbackContexts.put(requestId, callbackContext);
+ permissionStatuses.put(requestId, new JSONObject());
+ return Integer.valueOf(requestId);
+ }
+
+ protected String generateRandomRequestId(){
+ String requestId = null;
+
+ while(requestId == null){
+ requestId = generateRandom();
+ if(callbackContexts.containsKey(requestId)){
+ requestId = null;
+ }
+ }
+ return requestId;
+ }
+
+ protected String generateRandom(){
+ Random rn = new Random();
+ int random = rn.nextInt(1000000) + 1;
+ return Integer.toString(random);
+ }
+
+ protected String[] jsonArrayToStringArray(JSONArray array) throws JSONException{
+ if(array==null)
+ return null;
+
+ String[] arr=new String[array.length()];
+ for(int i=0; i= 24){
+ minVersion = applicationInfo.minSdkVersion;
+ }
+ }
+ return minVersion;
+ }
+
+
+ // https://stackoverflow.com/a/55946200/777265
+ protected String getNameForApiLevel(int apiLevel) throws Exception{
+ Field[] fields = Build.VERSION_CODES.class.getFields();
+ String codeName = "UNKNOWN";
+ for (Field field : fields) {
+ if (field.getInt(Build.VERSION_CODES.class) == apiLevel) {
+ codeName = field.getName();
+ }
+ }
+ return codeName;
+ }
+
+ protected String[] concatStrings(String[] A, String[] B) {
+ int aLen = A.length;
+ int bLen = B.length;
+ String[] C= new String[aLen+bLen];
+ System.arraycopy(A, 0, C, 0, aLen);
+ System.arraycopy(B, 0, C, aLen, bLen);
+ return C;
+ }
+
+ /************
+ * Overrides
+ ***********/
+
+ /**
+ * Callback received when a runtime permissions request has been completed.
+ * Retrieves the stateful Cordova context and permission statuses associated with the requestId,
+ * then updates the list of status based on the grantResults before passing the result back via the context.
+ *
+ * @param requestCode - ID that was used when requesting permissions
+ * @param permissions - list of permissions that were requested
+ * @param grantResults - list of flags indicating if above permissions were granted or denied
+ */
+ public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) throws JSONException {
+ String sRequestId = String.valueOf(requestCode);
+ Log.v(TAG, "Received result for permissions request id=" + sRequestId);
+ try {
+
+ CallbackContext context = getContextById(sRequestId);
+ JSONObject statuses = permissionStatuses.get(sRequestId);
+
+ for (int i = 0, len = permissions.length; i < len; i++) {
+ String androidPermission = permissions[i];
+ String permission = permissionsMap.get(androidPermission);
+ if(Build.VERSION.SDK_INT < 29 && permission.equals("ACCESS_BACKGROUND_LOCATION")){
+ // This version of Android doesn't support background location permission so use standard coarse location permission
+ permission = "ACCESS_COARSE_LOCATION";
+ }
+ if(Build.VERSION.SDK_INT < 29 && permission.equals("ACTIVITY_RECOGNITION")){
+ // This version of Android doesn't support activity recognition permission so check for body sensors permission
+ permission = "BODY_SENSORS";
+ }
+ String status;
+ if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
+ boolean showRationale = shouldShowRequestPermissionRationale(this.cordova.getActivity(), androidPermission);
+ if (!showRationale) {
+ if(isPermissionRequested(permission)){
+ // user denied WITH "never ask again"
+ status = Diagnostic.STATUS_DENIED_ALWAYS;
+ }else{
+ // The app doesn't have permission and the user has not been asked for the permission before
+ status = Diagnostic.STATUS_NOT_REQUESTED;
+ }
+ } else {
+ // user denied WITHOUT "never ask again"
+ status = Diagnostic.STATUS_DENIED_ONCE;
+ }
+ } else {
+ // Permission granted
+ status = Diagnostic.STATUS_GRANTED;
+ }
+ statuses.put(permission, status);
+ Log.v(TAG, "Authorisation for " + permission + " is " + statuses.get(permission));
+ clearRequest(requestCode);
+ }
+
+ Class> externalStorageClass = null;
+ try {
+ externalStorageClass = Class.forName(externalStorageClassName);
+ } catch( ClassNotFoundException e ){}
+
+ if(requestCode == GET_EXTERNAL_SD_CARD_DETAILS_PERMISSION_REQUEST && externalStorageClass != null){
+ Method method = externalStorageClass.getMethod("onReceivePermissionResult");
+ method.invoke(null);
+ }else{
+ context.success(statuses);
+ }
+ }catch(Exception e ) {
+ handleError("Exception occurred onRequestPermissionsResult: ".concat(e.getMessage()), requestCode);
+ }
+ }
+
+}
diff --git a/cordova-plugin-moodleapp/src/android/Diagnostic_Location.java b/cordova-plugin-moodleapp/src/android/Diagnostic_Location.java
new file mode 100644
index 000000000..53a9e54b8
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/android/Diagnostic_Location.java
@@ -0,0 +1,318 @@
+// (C) Copyright 2015 Moodle Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.moodle.moodlemobile;
+
+/*
+ * Imports
+ */
+import org.apache.cordova.CordovaWebView;
+import org.apache.cordova.CallbackContext;
+import org.apache.cordova.CordovaPlugin;
+import org.apache.cordova.CordovaInterface;
+import org.apache.cordova.PluginResult;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import android.content.BroadcastReceiver;
+import android.content.IntentFilter;
+import android.location.LocationManager;
+import android.os.Build;
+import android.util.Log;
+
+import android.content.Context;
+import android.content.Intent;
+import android.provider.Settings;
+
+/**
+ * Diagnostic plugin implementation for Android
+ */
+public class Diagnostic_Location extends CordovaPlugin{
+
+
+ /*************
+ * Constants *
+ *************/
+
+ /**
+ * Tag for debug log messages
+ */
+ public static final String TAG = "Diagnostic_Location";
+
+ private static String gpsLocationPermission = "ACCESS_FINE_LOCATION";
+ private static String networkLocationPermission = "ACCESS_COARSE_LOCATION";
+ private static String backgroundLocationPermission = "ACCESS_BACKGROUND_LOCATION";
+
+
+ private static final String LOCATION_MODE_HIGH_ACCURACY = "high_accuracy";
+ private static final String LOCATION_MODE_DEVICE_ONLY = "device_only";
+ private static final String LOCATION_MODE_BATTERY_SAVING = "battery_saving";
+ private static final String LOCATION_MODE_OFF = "location_off";
+ private static final String LOCATION_MODE_UNKNOWN = "unknown";
+
+ /*************
+ * Variables *
+ *************/
+
+ /**
+ * Singleton class instance
+ */
+ public static Diagnostic_Location instance = null;
+
+ private Diagnostic diagnostic;
+
+ public static LocationManager locationManager;
+
+ /**
+ * Current Cordova callback context (on this thread)
+ */
+ protected CallbackContext currentContext;
+
+ private String currentLocationMode = null;
+
+ /*************
+ * Public API
+ ************/
+
+ /**
+ * Constructor.
+ */
+ public Diagnostic_Location() {}
+
+ /**
+ * Sets the context of the Command. This can then be used to do things like
+ * get file paths associated with the Activity.
+ *
+ * @param cordova The context of the main Activity.
+ * @param webView The CordovaWebView Cordova is running in.
+ */
+ public void initialize(CordovaInterface cordova, CordovaWebView webView) {
+ Log.d(TAG, "initialize()");
+ instance = this;
+ diagnostic = Diagnostic.getInstance();
+
+ try {
+ diagnostic.applicationContext.registerReceiver(locationProviderChangedReceiver, new IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION));
+ locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
+ }catch(Exception e){
+ diagnostic.logWarning("Unable to register Location Provider Change receiver: " + e.getMessage());
+ }
+
+ try {
+ currentLocationMode = getLocationModeName();
+ }catch(Exception e){
+ diagnostic.logWarning("Unable to get initial location mode: " + e.getMessage());
+ }
+
+ super.initialize(cordova, webView);
+ }
+
+ /**
+ * Called on destroying activity
+ */
+ public void onDestroy() {
+ try {
+ diagnostic.applicationContext.unregisterReceiver(locationProviderChangedReceiver);
+ }catch(Exception e){
+ diagnostic.logWarning("Unable to unregister Location Provider Change receiver: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Executes the request and returns PluginResult.
+ *
+ * @param action The action to execute.
+ * @param args JSONArry of arguments for the plugin.
+ * @param callbackContext The callback id used when calling back into JavaScript.
+ * @return True if the action was valid, false if not.
+ */
+ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+ Diagnostic.instance.currentContext = currentContext = callbackContext;
+
+ try {
+ if (action.equals("switchToLocationSettings")){
+ switchToLocationSettings();
+ callbackContext.success();
+ } else if(action.equals("isLocationAvailable")) {
+ callbackContext.success(isGpsLocationAvailable() || isNetworkLocationAvailable() ? 1 : 0);
+ } else if(action.equals("isLocationEnabled")) {
+ callbackContext.success(isGpsLocationEnabled() || isNetworkLocationEnabled() ? 1 : 0);
+ } else if(action.equals("isGpsLocationAvailable")) {
+ callbackContext.success(isGpsLocationAvailable() ? 1 : 0);
+ } else if(action.equals("isNetworkLocationAvailable")) {
+ callbackContext.success(isNetworkLocationAvailable() ? 1 : 0);
+ } else if(action.equals("isGpsLocationEnabled")) {
+ callbackContext.success(isGpsLocationEnabled() ? 1 : 0);
+ } else if(action.equals("isNetworkLocationEnabled")) {
+ callbackContext.success(isNetworkLocationEnabled() ? 1 : 0);
+ } else if(action.equals("getLocationMode")) {
+ callbackContext.success(getLocationModeName());
+ } else if(action.equals("requestLocationAuthorization")) {
+ requestLocationAuthorization(args, callbackContext);
+ }else {
+ diagnostic.handleError("Invalid action");
+ return false;
+ }
+ }catch(Exception e ) {
+ diagnostic.handleError("Exception occurred: ".concat(e.getMessage()));
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isGpsLocationAvailable() throws Exception {
+ boolean result = isGpsLocationEnabled() && isLocationAuthorized();
+ diagnostic.logDebug("GPS location available: " + result);
+ return result;
+ }
+
+ public boolean isGpsLocationEnabled() throws Exception {
+ int mode = getLocationMode();
+ boolean result = (mode == 3 || mode == 1);
+ diagnostic.logDebug("GPS location setting enabled: " + result);
+ return result;
+ }
+
+ public boolean isNetworkLocationAvailable() throws Exception {
+ boolean result = isNetworkLocationEnabled() && isLocationAuthorized();
+ diagnostic.logDebug("Network location available: " + result);
+ return result;
+ }
+
+ public boolean isNetworkLocationEnabled() throws Exception {
+ int mode = getLocationMode();
+ boolean result = (mode == 3 || mode == 2);
+ diagnostic.logDebug("Network location setting enabled: " + result);
+ return result;
+ }
+
+ public String getLocationModeName() throws Exception {
+ String modeName;
+ int mode = getLocationMode();
+ switch(mode){
+ case Settings.Secure.LOCATION_MODE_HIGH_ACCURACY:
+ modeName = LOCATION_MODE_HIGH_ACCURACY;
+ break;
+ case Settings.Secure.LOCATION_MODE_SENSORS_ONLY:
+ modeName = LOCATION_MODE_DEVICE_ONLY;
+ break;
+ case Settings.Secure.LOCATION_MODE_BATTERY_SAVING:
+ modeName = LOCATION_MODE_BATTERY_SAVING;
+ break;
+ case Settings.Secure.LOCATION_MODE_OFF:
+ modeName = LOCATION_MODE_OFF;
+ break;
+ default:
+ modeName = LOCATION_MODE_UNKNOWN;
+ }
+ return modeName;
+ }
+
+ public void notifyLocationStateChange(){
+ try {
+ String newMode = getLocationModeName();
+ if(!newMode.equals(currentLocationMode)){
+ diagnostic.logDebug("Location mode change to: " + newMode);
+ diagnostic.executePluginJavascript("location._onLocationStateChange(\"" + newMode +"\");");
+ currentLocationMode = newMode;
+ }
+ }catch(Exception e){
+ diagnostic.logError("Error retrieving current location mode on location state change: "+e.toString());
+ }
+ }
+
+ public void switchToLocationSettings() {
+ diagnostic.logDebug("Switch to Location Settings");
+ Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+ cordova.getActivity().startActivity(settingsIntent);
+ }
+
+ public void requestLocationAuthorization(JSONArray args, CallbackContext callbackContext) throws Exception{
+ JSONArray permissionsToRequest = new JSONArray();
+ boolean shouldRequestBackground = args.getBoolean(0);
+ boolean shouldRequestPrecise = args.getBoolean(1);
+
+ permissionsToRequest.put(networkLocationPermission);
+ if(shouldRequestPrecise || Build.VERSION.SDK_INT < 31){
+ permissionsToRequest.put(gpsLocationPermission);
+ }
+
+ if(shouldRequestBackground && Build.VERSION.SDK_INT >= 29 ){
+ permissionsToRequest.put(backgroundLocationPermission);
+ }
+
+ int requestId = Diagnostic.instance.storeContextByRequestId(callbackContext);
+ Diagnostic.instance._requestRuntimePermissions(permissionsToRequest, requestId);
+
+ PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
+ result.setKeepCallback(true);
+ callbackContext.sendPluginResult(result);
+ }
+
+
+
+ /************
+ * Internals
+ ***********/
+ /**
+ * Returns current location mode
+ */
+ private int getLocationMode() throws Exception {
+ int mode;
+ if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 28){ // Kitkat to Oreo, Settings.Secute.LOCATION_MODE was deprecated in Pie (https://developer.android.com/reference/android/provider/Settings.Secure#LOCATION_MODE)
+ mode = Settings.Secure.getInt(this.cordova.getActivity().getContentResolver(), Settings.Secure.LOCATION_MODE);
+ }else{ // Pre-Kitkat and post-Oreo
+ if(isLocationProviderEnabled(LocationManager.GPS_PROVIDER) && isLocationProviderEnabled(LocationManager.NETWORK_PROVIDER)){
+ mode = 3;
+ } else if(isLocationProviderEnabled(LocationManager.GPS_PROVIDER)){
+ mode = 1;
+ } else if(isLocationProviderEnabled(LocationManager.NETWORK_PROVIDER)){
+ mode = 2;
+ }else{
+ mode = 0;
+ }
+ }
+ return mode;
+ }
+
+ private boolean isLocationAuthorized() throws Exception {
+ boolean authorized = diagnostic.hasRuntimePermission(diagnostic.permissionsMap.get(gpsLocationPermission)) || diagnostic.hasRuntimePermission(diagnostic.permissionsMap.get(networkLocationPermission));
+ Log.v(TAG, "Location permission is "+(authorized ? "authorized" : "unauthorized"));
+ return authorized;
+ }
+
+ private boolean isLocationProviderEnabled(String provider) {
+ return locationManager.isProviderEnabled(provider);
+ }
+
+
+ /************
+ * Overrides
+ ***********/
+
+ protected final BroadcastReceiver locationProviderChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ try {
+ final String action = intent.getAction();
+ if(instance != null && action.equals(LocationManager.PROVIDERS_CHANGED_ACTION)){
+ Log.v(TAG, "onReceiveLocationProviderChange");
+ instance.notifyLocationStateChange();
+ }
+ } catch (Exception e) {
+ diagnostic.logError("Error receiving location provider state change: "+e.toString());
+ }
+ }
+ };
+}
diff --git a/cordova-plugin-moodleapp/src/ios/Diagnostic.h b/cordova-plugin-moodleapp/src/ios/Diagnostic.h
new file mode 100644
index 000000000..4a7791247
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/ios/Diagnostic.h
@@ -0,0 +1,63 @@
+/*
+ * Diagnostic.h
+ * Diagnostic Plugin - Core Module
+ *
+ * Copyright (c) 2015 Working Edge Ltd.
+ * Copyright (c) 2012 AVANTIC ESTUDIO DE INGENIEROS
+ */
+
+#import
+#import
+#import
+
+#import
+#import
+#import
+
+// Public constants
+extern NSString*const UNKNOWN;
+
+extern NSString*const AUTHORIZATION_NOT_DETERMINED;
+extern NSString*const AUTHORIZATION_DENIED;
+extern NSString*const AUTHORIZATION_GRANTED;
+extern NSString*const AUTHORIZATION_PROVISIONAL;
+extern NSString*const AUTHORIZATION_EPHEMERAL;
+extern NSString*const AUTHORIZATION_LIMITED;
+
+@interface Diagnostic : CDVPlugin
+
+@property (nonatomic) float osVersion;
+@property (nonatomic) BOOL debugEnabled;
+
+// Plugin API
+- (void) enableDebug: (CDVInvokedUrlCommand*)command;
+- (void) switchToSettings: (CDVInvokedUrlCommand*)command;
+- (void) getBackgroundRefreshStatus: (CDVInvokedUrlCommand*)command;
+- (void) getArchitecture: (CDVInvokedUrlCommand*)command;
+- (void) getCurrentBatteryLevel: (CDVInvokedUrlCommand*)command;
+- (void) getDeviceOSVersion: (CDVInvokedUrlCommand*)command;
+- (void) getBuildOSVersion: (CDVInvokedUrlCommand*)command;
+- (void) isMobileDataEnabled: (CDVInvokedUrlCommand*)command;
+
+// Utilities
++ (id) getInstance;
+- (void) sendPluginResult: (CDVPluginResult*)result :(CDVInvokedUrlCommand*)command;
+- (void) sendPluginResultSuccess:(CDVInvokedUrlCommand*)command;
+- (void) sendPluginNoResultAndKeepCallback:(CDVInvokedUrlCommand*)command;
+- (void) sendPluginResultBool: (BOOL)result :(CDVInvokedUrlCommand*)command;
+- (void) sendPluginResultString: (NSString*)result :(CDVInvokedUrlCommand*)command;
+- (void) sendPluginError: (NSString*) errorMessage :(CDVInvokedUrlCommand*)command;
+- (void) handlePluginException: (NSException*) exception :(CDVInvokedUrlCommand*)command;
+- (void)executeGlobalJavascript: (NSString*)jsString;
+- (NSString*) arrayToJsonString:(NSArray*)inputArray;
+- (NSString*) objectToJsonString:(NSDictionary*)inputObject;
+- (NSArray*) jsonStringToArray:(NSString*)jsonStr;
+- (NSDictionary*) jsonStringToDictionary:(NSString*)jsonStr;
+- (bool)isNull: (NSString*)str;
+- (void)logDebug: (NSString*)msg;
+- (void)logError: (NSString*)msg;
+- (NSString*)escapeDoubleQuotes: (NSString*)str;
+- (void) setSetting: (NSString*)key forValue:(id)value;
+- (id) getSetting: (NSString*) key;
+
+@end
diff --git a/cordova-plugin-moodleapp/src/ios/Diagnostic.m b/cordova-plugin-moodleapp/src/ios/Diagnostic.m
new file mode 100644
index 000000000..329391ece
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/ios/Diagnostic.m
@@ -0,0 +1,379 @@
+/*
+ * Diagnostic.m
+ * Diagnostic Plugin - Core Module
+ *
+ * Copyright (c) 2015 Working Edge Ltd.
+ * Copyright (c) 2012 AVANTIC ESTUDIO DE INGENIEROS
+ */
+
+#import "Diagnostic.h"
+#import
+
+@implementation Diagnostic
+
+// Public constants
+NSString*const UNKNOWN = @"unknown";
+
+NSString*const AUTHORIZATION_NOT_DETERMINED = @"not_determined";
+NSString*const AUTHORIZATION_DENIED = @"denied_always";
+NSString*const AUTHORIZATION_GRANTED = @"authorized";
+NSString*const AUTHORIZATION_PROVISIONAL = @"provisional"; // Remote Notifications
+NSString*const AUTHORIZATION_EPHEMERAL = @"ephemeral"; // Remote Notifications
+NSString*const AUTHORIZATION_LIMITED = @"limited"; // Photo Library
+
+// Internal constants
+static NSString*const LOG_TAG = @"Diagnostic[native]";
+
+static NSString*const CPU_ARCH_ARMv6 = @"ARMv6";
+static NSString*const CPU_ARCH_ARMv7 = @"ARMv7";
+static NSString*const CPU_ARCH_ARMv8 = @"ARMv8";
+static NSString*const CPU_ARCH_X86 = @"X86";
+static NSString*const CPU_ARCH_X86_64 = @"X86_64";
+
+// Internal properties
+static Diagnostic* diagnostic = nil;
+static CTCellularData* cellularData;
+
+/********************************/
+#pragma mark - Public static functions
+/********************************/
++ (id) getInstance{
+ return diagnostic;
+}
+
+
+/********************************/
+#pragma mark - Plugin API
+/********************************/
+
+-(void)enableDebug:(CDVInvokedUrlCommand*)command{
+ self.debugEnabled = true;
+ [self logDebug:@"Debug enabled"];
+}
+
+#pragma mark - Settings
+- (void) switchToSettings: (CDVInvokedUrlCommand*)command
+{
+ @try {
+ [[UIApplication sharedApplication] openURL:[NSURL URLWithString: UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {
+ if (success) {
+ [self sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] :command];
+ }else{
+ [self sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR] :command];
+ }
+ }];
+ }
+ @catch (NSException *exception) {
+ [self handlePluginException:exception :command];
+ }
+}
+
+#pragma mark - Background refresh
+- (void) getBackgroundRefreshStatus: (CDVInvokedUrlCommand*)command
+{
+ UIBackgroundRefreshStatus _status;
+ @try {
+ // Must run on UI thread
+ _status = [[UIApplication sharedApplication] backgroundRefreshStatus];
+ }@catch (NSException *exception) {
+ [self handlePluginException:exception :command];
+ return;
+ }
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* status;
+
+ if (_status == UIBackgroundRefreshStatusAvailable) {
+ status = AUTHORIZATION_GRANTED;
+ [self logDebug:@"Background updates are available for the app."];
+ }else if(_status == UIBackgroundRefreshStatusDenied){
+ status = AUTHORIZATION_DENIED;
+ [self logDebug:@"The user explicitly disabled background behavior for this app or for the whole system."];
+ }else if(_status == UIBackgroundRefreshStatusRestricted){
+ status = @"restricted";
+ [self logDebug:@"Background updates are unavailable and the user cannot enable them again. For example, this status can occur when parental controls are in effect for the current user."];
+ }
+ [self sendPluginResultString:status:command];
+ }
+ @catch (NSException *exception) {
+ [self handlePluginException:exception :command];
+ }
+ }];
+}
+
+
+/********************************/
+#pragma mark - Internal functions
+/********************************/
+
+- (void)pluginInitialize {
+
+ [super pluginInitialize];
+
+ diagnostic = self;
+
+ self.debugEnabled = false;
+ self.osVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
+ cellularData = [[CTCellularData alloc] init];
+}
+
+// https://stackoverflow.com/a/38441011/777265
+- (void) getArchitecture: (CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* cpuArch = UNKNOWN;
+
+ size_t size;
+ cpu_type_t type;
+ cpu_subtype_t subtype;
+ size = sizeof(type);
+ sysctlbyname("hw.cputype", &type, &size, NULL, 0);
+
+ size = sizeof(subtype);
+ sysctlbyname("hw.cpusubtype", &subtype, &size, NULL, 0);
+
+ // values for cputype and cpusubtype defined in mach/machine.h
+ if (type == CPU_TYPE_X86_64) {
+ cpuArch = CPU_ARCH_X86_64;
+ } else if (type == CPU_TYPE_X86) {
+ cpuArch = CPU_ARCH_X86;
+ } else if (type == CPU_TYPE_ARM64) {
+ cpuArch = CPU_ARCH_ARMv8;
+ } else if (type == CPU_TYPE_ARM) {
+ switch(subtype){
+ case CPU_SUBTYPE_ARM_V6:
+ cpuArch = CPU_ARCH_ARMv6;
+ break;
+ case CPU_SUBTYPE_ARM_V7:
+ cpuArch = CPU_ARCH_ARMv7;
+ break;
+ case CPU_SUBTYPE_ARM_V8:
+ cpuArch = CPU_ARCH_ARMv8;
+ break;
+ }
+ }
+ [self logDebug:[NSString stringWithFormat:@"Current CPU architecture: %@", cpuArch]];
+ [self sendPluginResultString:cpuArch:command];
+ }@catch (NSException *exception) {
+ [self handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) getCurrentBatteryLevel: (CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ UIDevice* currentDevice = [UIDevice currentDevice];
+ [currentDevice setBatteryMonitoringEnabled:true];
+ int batteryLevel = (int)([currentDevice batteryLevel]*100);
+ [self logDebug:[NSString stringWithFormat:@"Battery level: %d", batteryLevel]];
+ [self sendPluginResultInt:batteryLevel:command];
+ [currentDevice setBatteryMonitoringEnabled:false];
+ }@catch (NSException *exception) {
+ [self handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) getDeviceOSVersion: (CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* s_version = [UIDevice currentDevice].systemVersion;
+ float f_version = [s_version floatValue];
+
+ NSDictionary* details = @{
+ @"version": s_version,
+ @"apiLevel" : [NSNumber numberWithFloat:f_version*10000],
+ @"apiName": s_version
+ };
+
+ [self sendPluginResultObject:details:command];
+ }@catch (NSException *exception) {
+ [self handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) getBuildOSVersion: (CDVInvokedUrlCommand*)command {
+ [self.commandDelegate runInBackground:^{
+ @try {
+ int i_min_version = __IPHONE_OS_VERSION_MIN_REQUIRED;
+ NSString* s_min_version = [NSString stringWithFormat:@"%.01f", (float) i_min_version/10000];
+ int i_target_version = __IPHONE_OS_VERSION_MAX_ALLOWED;
+ NSString* s_target_version = [NSString stringWithFormat:@"%.01f", (float) i_target_version/10000];
+
+ NSDictionary* details = @{
+ @"targetApiLevel": [NSNumber numberWithInt:i_target_version],
+ @"targetApiName": s_target_version,
+ @"minApiLevel": [NSNumber numberWithInt:i_min_version],
+ @"minApiName": s_min_version
+ };
+
+ [self sendPluginResultObject:details:command];
+ }@catch (NSException *exception) {
+ [self handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) isMobileDataEnabled: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ bool isEnabled = cellularData.restrictedState == kCTCellularDataNotRestricted;;
+ [diagnostic sendPluginResultBool:isEnabled :command];
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+
+/********************************/
+#pragma mark - Send results
+/********************************/
+
+- (void) sendPluginResult: (CDVPluginResult*)result :(CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void) sendPluginResultSuccess:(CDVInvokedUrlCommand*)command{
+ [self.commandDelegate sendPluginResult:[CDVPluginResult resultWithStatus:CDVCommandStatus_OK] callbackId:command.callbackId];
+}
+
+- (void) sendPluginNoResultAndKeepCallback:(CDVInvokedUrlCommand*)command {
+ CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
+ [pluginResult setKeepCallbackAsBool:YES];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void) sendPluginResultBool: (BOOL)result :(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult;
+ if(result) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:1];
+ } else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:0];
+ }
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void) sendPluginResultString: (NSString*)result :(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:result];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void) sendPluginResultInt: (int)result :(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:result];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void) sendPluginResultObject: (NSDictionary*)result :(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void) sendPluginError: (NSString*) errorMessage :(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMessage];
+ [self logError:errorMessage];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void) handlePluginException: (NSException*) exception :(CDVInvokedUrlCommand*)command
+{
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:exception.reason];
+ [self logError:[NSString stringWithFormat:@"EXCEPTION: %@", exception.reason]];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
+}
+
+- (void)executeGlobalJavascript: (NSString*)jsString
+{
+ [self.commandDelegate evalJs:jsString];
+}
+
+- (NSString*) arrayToJsonString:(NSArray*)inputArray
+{
+ NSError* error;
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:inputArray options:NSJSONWritingPrettyPrinted error:&error];
+ NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ jsonString = [[jsonString componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] componentsJoinedByString:@""];
+ return jsonString;
+}
+
+- (NSString*) objectToJsonString:(NSDictionary*)inputObject
+{
+ NSError* error;
+ NSData* jsonData = [NSJSONSerialization dataWithJSONObject:inputObject options:NSJSONWritingPrettyPrinted error:&error];
+ NSString* jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
+ return jsonString;
+}
+
+- (NSArray*) jsonStringToArray:(NSString*)jsonStr
+{
+ NSError* error = nil;
+ NSArray* array = [NSJSONSerialization JSONObjectWithData:[jsonStr dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&error];
+ if (error != nil){
+ array = nil;
+ }
+ return array;
+}
+
+- (NSDictionary*) jsonStringToDictionary:(NSString*)jsonStr
+{
+ return (NSDictionary*) [self jsonStringToArray:jsonStr];
+}
+
+- (bool)isNull: (NSString*)str
+{
+ return str == nil || str == (id)[NSNull null] || str.length == 0 || [str isEqual: @""];
+}
+
+
+/********************************/
+#pragma mark - utility functions
+/********************************/
+
+- (void)logDebug: (NSString*)msg
+{
+ if(self.debugEnabled){
+ NSLog(@"%@: %@", LOG_TAG, msg);
+ NSString* jsString = [NSString stringWithFormat:@"console.log(\"%@: %@\")", LOG_TAG, [self escapeDoubleQuotes:msg]];
+ [self executeGlobalJavascript:jsString];
+ }
+}
+
+- (void)logError: (NSString*)msg
+{
+ NSLog(@"%@ ERROR: %@", LOG_TAG, msg);
+ if(self.debugEnabled){
+ NSString* jsString = [NSString stringWithFormat:@"console.error(\"%@: %@\")", LOG_TAG, [self escapeDoubleQuotes:msg]];
+ [self executeGlobalJavascript:jsString];
+ }
+}
+
+- (NSString*)escapeDoubleQuotes: (NSString*)str
+{
+ NSString *result =[str stringByReplacingOccurrencesOfString: @"\"" withString: @"\\\""];
+ return result;
+}
+
+- (void) setSetting: (NSString*)key forValue:(id)value
+{
+ [[NSUserDefaults standardUserDefaults] setObject:value forKey:key];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+- (id) getSetting: (NSString*) key
+{
+ return [[NSUserDefaults standardUserDefaults] objectForKey:key];
+}
+
+@end
+
+
diff --git a/cordova-plugin-moodleapp/src/ios/Diagnostic_Location.h b/cordova-plugin-moodleapp/src/ios/Diagnostic_Location.h
new file mode 100644
index 000000000..358bcdd4f
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/ios/Diagnostic_Location.h
@@ -0,0 +1,31 @@
+/*
+ * Diagnostic_Location.h
+ * Diagnostic Plugin - Location Module
+ *
+ * Copyright (c) 2018 Working Edge Ltd.
+ * Copyright (c) 2012 AVANTIC ESTUDIO DE INGENIEROS
+ */
+
+#import
+#import
+#import "Diagnostic.h"
+#import
+
+
+
+@interface Diagnostic_Location : CDVPlugin
+
+@property (strong, nonatomic) CLLocationManager* locationManager;
+@property (nonatomic, retain) NSString* locationRequestCallbackId;
+@property (nonatomic, retain) NSString* currentLocationAuthorizationStatus;
+@property (nonatomic, retain) NSString* currentLocationAccuracyAuthorization;
+
+- (void) isLocationAvailable: (CDVInvokedUrlCommand*)command;
+- (void) isLocationEnabled: (CDVInvokedUrlCommand*)command;
+- (void) isLocationAuthorized: (CDVInvokedUrlCommand*)command;
+- (void) getLocationAuthorizationStatus: (CDVInvokedUrlCommand*)command;
+- (void) getLocationAccuracyAuthorization: (CDVInvokedUrlCommand*)command;
+- (void) requestLocationAuthorization: (CDVInvokedUrlCommand*)command;
+- (void) requestTemporaryFullAccuracyAuthorization: (CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/cordova-plugin-moodleapp/src/ios/Diagnostic_Location.m b/cordova-plugin-moodleapp/src/ios/Diagnostic_Location.m
new file mode 100644
index 000000000..c1f1b473c
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/ios/Diagnostic_Location.m
@@ -0,0 +1,286 @@
+/*
+ * Diagnostic_Location.m
+ * Diagnostic Plugin - Location Module
+ *
+ * Copyright (c) 2018 Working Edge Ltd.
+ * Copyright (c) 2012 AVANTIC ESTUDIO DE INGENIEROS
+ */
+
+#import "Diagnostic_Location.h"
+
+@implementation Diagnostic_Location
+
+// Internal reference to Diagnostic singleton instance
+static Diagnostic* diagnostic;
+
+// Internal constants
+static NSString*const LOG_TAG = @"Diagnostic_Location[native]";
+
+
+/********************************/
+#pragma mark - Plugin API
+/********************************/
+
+- (void) isLocationAvailable: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ [diagnostic sendPluginResultBool:[CLLocationManager locationServicesEnabled] && [self isLocationAuthorized] :command];
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) isLocationEnabled: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ [diagnostic sendPluginResultBool:[CLLocationManager locationServicesEnabled] :command];
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+
+- (void) isLocationAuthorized: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ [diagnostic sendPluginResultBool:[self isLocationAuthorized] :command];
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) getLocationAuthorizationStatus: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* status = [self getLocationAuthorizationStatusAsString:[self getAuthorizationStatus]];
+ [diagnostic logDebug:[NSString stringWithFormat:@"Location authorization status is: %@", status]];
+ [diagnostic sendPluginResultString:status:command];
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) requestLocationAuthorization: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ if ([CLLocationManager instancesRespondToSelector:@selector(requestWhenInUseAuthorization)])
+ {
+ BOOL always = [[command argumentAtIndex:0] boolValue];
+ if(always){
+ NSAssert([[[NSBundle mainBundle] infoDictionary] valueForKey:@"NSLocationAlwaysAndWhenInUseUsageDescription"], @"Your app must have a value for NSLocationAlwaysAndWhenInUseUsageDescription in its Info.plist");
+ [self.locationManager requestAlwaysAuthorization];
+ [diagnostic logDebug:@"Requesting location authorization: always"];
+ }else{
+ NSAssert([[[NSBundle mainBundle] infoDictionary] valueForKey:@"NSLocationWhenInUseUsageDescription"], @"Your app must have a value for NSLocationWhenInUseUsageDescription in its Info.plist");
+ [self.locationManager requestWhenInUseAuthorization];
+ [diagnostic logDebug:@"Requesting location authorization: when in use"];
+ }
+ }
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ self.locationRequestCallbackId = command.callbackId;
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_NO_RESULT];
+ [pluginResult setKeepCallback:[NSNumber numberWithBool:YES]];
+ [diagnostic sendPluginResult:pluginResult :command];
+ }];
+}
+
+- (void) getLocationAccuracyAuthorization: (CDVInvokedUrlCommand*)command{
+ [self.commandDelegate runInBackground:^{
+ @try {
+
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+ if ([CLLocationManager instancesRespondToSelector:@selector(requestTemporaryFullAccuracyAuthorizationWithPurposeKey:completion:)]){
+ NSString* locationAccuracyAuthorization = [self getLocationAccuracyAuthorizationAsString:[self.locationManager accuracyAuthorization]];
+ [diagnostic logDebug:[NSString stringWithFormat:@"Location accuracy authorization is: %@", locationAccuracyAuthorization]];
+ [diagnostic sendPluginResultString:locationAccuracyAuthorization:command];
+ }else{
+ [diagnostic logDebug:@"Location accuracy authorization is not available on device running iOS <14"];
+ [diagnostic sendPluginResultString:@"full":command];
+ }
+#else
+ [diagnostic logDebug:@"Location accuracy authorization is not available in builds with iOS SDK <14"];
+ [diagnostic sendPluginResultString:@"full":command];
+#endif
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) requestTemporaryFullAccuracyAuthorization: (CDVInvokedUrlCommand*)command{
+ [self.commandDelegate runInBackground:^{
+ @try {
+
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+ if ([CLLocationManager instancesRespondToSelector:@selector(requestTemporaryFullAccuracyAuthorizationWithPurposeKey:completion:)]){
+ NSAssert([[[NSBundle mainBundle] infoDictionary] valueForKey:@"NSLocationTemporaryUsageDescriptionDictionary"], @"For iOS 14 and above, your app must have a value for NSLocationTemporaryUsageDescriptionDictionary in its Info.plist");
+ NSString* purpose = [command argumentAtIndex:0];
+ [self.locationManager requestTemporaryFullAccuracyAuthorizationWithPurposeKey:purpose completion:^(NSError* error){
+ if(error != nil){
+ [diagnostic sendPluginError:[NSString stringWithFormat:@"Error when requesting temporary full location accuracy authorization: %@", error] :command];
+ }else{
+ NSString* locationAccuracyAuthorization = [self getLocationAccuracyAuthorizationAsString:[self.locationManager accuracyAuthorization]];
+ [diagnostic sendPluginResultString:locationAccuracyAuthorization :command];
+ }
+ }];
+ }else{
+ [diagnostic sendPluginError:@"requestTemporaryFullAccuracyAuthorization is not available on device running iOS <14":command];
+ }
+#else
+ [diagnostic sendPluginError:@"requestTemporaryFullAccuracyAuthorization is not available in builds with iOS SDK <14":command];
+#endif
+
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+/********************************/
+#pragma mark - Internals
+/********************************/
+
+- (void)pluginInitialize {
+
+ [super pluginInitialize];
+
+ diagnostic = [Diagnostic getInstance];
+
+ self.locationRequestCallbackId = nil;
+ self.currentLocationAuthorizationStatus = nil;
+ self.locationManager = [[CLLocationManager alloc] init];
+ self.locationManager.delegate = self;
+}
+
+- (NSString*) getLocationAuthorizationStatusAsString: (CLAuthorizationStatus)authStatus
+{
+ NSString* status;
+ if(authStatus == kCLAuthorizationStatusDenied || authStatus == kCLAuthorizationStatusRestricted){
+ status = AUTHORIZATION_DENIED;
+ }else if(authStatus == kCLAuthorizationStatusNotDetermined){
+ status = AUTHORIZATION_NOT_DETERMINED;
+ }else if(authStatus == kCLAuthorizationStatusAuthorizedAlways){
+ status = AUTHORIZATION_GRANTED;
+ }else if(authStatus == kCLAuthorizationStatusAuthorizedWhenInUse){
+ status = @"authorized_when_in_use";
+ }
+ return status;
+}
+
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+- (NSString*) getLocationAccuracyAuthorizationAsString: (CLAccuracyAuthorization)accuracyAuthorization
+{
+ NSString* accuracy;
+ if(accuracyAuthorization == CLAccuracyAuthorizationFullAccuracy){
+ accuracy = @"full";
+ }else{
+ accuracy = @"reduced";
+ }
+ return accuracy;
+}
+#endif
+
+- (BOOL) isLocationAuthorized
+{
+ CLAuthorizationStatus authStatus = [self getAuthorizationStatus];
+ NSString* status = [self getLocationAuthorizationStatusAsString:authStatus];
+ if([status isEqual: AUTHORIZATION_GRANTED] || [status isEqual: @"authorized_when_in_use"]) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+-(CLAuthorizationStatus) getAuthorizationStatus{
+ CLAuthorizationStatus authStatus;
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+ if ([CLLocationManager instancesRespondToSelector:@selector(authorizationStatus)]){
+ authStatus = [self.locationManager authorizationStatus];
+ }else{
+ authStatus = [CLLocationManager authorizationStatus];
+ }
+#else
+ authStatus = [CLLocationManager authorizationStatus];
+#endif
+ return authStatus;
+}
+
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+// Note: if built with Xcode >=12 (iOS >=14 SDK) but device is running on iOS <=13, this will not be invoked
+-(void)locationManagerDidChangeAuthorization:(CLLocationManager *)manager{
+ // Location authorization status
+ [self reportChangeAuthorizationStatus:[self.locationManager authorizationStatus]];
+
+ // Location accuracy authorization
+ NSString* locationAccuracyAuthorization = [self getLocationAccuracyAuthorizationAsString:[self.locationManager accuracyAuthorization]];
+ BOOL locationAccuracyAuthorizationChanged = false;
+ if(self.currentLocationAccuracyAuthorization != nil && ![locationAccuracyAuthorization isEqual: self.currentLocationAccuracyAuthorization]){
+ locationAccuracyAuthorizationChanged = true;
+ }
+ self.currentLocationAccuracyAuthorization = locationAccuracyAuthorization;
+
+ if(locationAccuracyAuthorizationChanged){
+ [diagnostic logDebug:[NSString stringWithFormat:@"Location accuracy authorization changed to: %@", locationAccuracyAuthorization]];
+
+ [diagnostic executeGlobalJavascript:[NSString stringWithFormat:@"cordova.plugins.diagnostic.location._onLocationAccuracyAuthorizationChange(\"%@\");", locationAccuracyAuthorization]];
+ }
+}
+#endif
+
+- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)authStatus {
+#if defined(__IPHONE_14_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0
+ if ([CLLocationManager instancesRespondToSelector:@selector(authorizationStatus)]){
+ // Build SDK & device using iOS >=14 so locationManagerDidChangeAuthorization will be invoked
+ }else{
+ // Build SDK using iOS >=14 but device running iOS <=13
+ [self reportChangeAuthorizationStatus:authStatus];
+ }
+#else
+ // Device may be running iOS >=14 but build SDK is iOS <=13
+ [self reportChangeAuthorizationStatus:authStatus];
+#endif
+}
+
+
+- (void)reportChangeAuthorizationStatus:(CLAuthorizationStatus)authStatus{
+
+ NSString* locationAuthorizationStatus = [self getLocationAuthorizationStatusAsString:authStatus];
+ BOOL locationAuthorizationStatusChanged = false;
+ if(self.currentLocationAuthorizationStatus != nil && ![locationAuthorizationStatus isEqual: self.currentLocationAuthorizationStatus]){
+ locationAuthorizationStatusChanged = true;
+ }
+ self.currentLocationAuthorizationStatus = locationAuthorizationStatus;
+
+ if(locationAuthorizationStatusChanged){
+ [diagnostic logDebug:[NSString stringWithFormat:@"Location authorization status changed to: %@", locationAuthorizationStatus]];
+
+ if(self.locationRequestCallbackId != nil){
+ CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:locationAuthorizationStatus];
+ [self.commandDelegate sendPluginResult:pluginResult callbackId:self.locationRequestCallbackId];
+ self.locationRequestCallbackId = nil;
+ }
+
+ [diagnostic executeGlobalJavascript:[NSString stringWithFormat:@"cordova.plugins.diagnostic.location._onLocationStateChange(\"%@\");", locationAuthorizationStatus]];
+ }
+}
+
+@end
+
diff --git a/cordova-plugin-moodleapp/src/ios/Diagnostic_Microphone.h b/cordova-plugin-moodleapp/src/ios/Diagnostic_Microphone.h
new file mode 100644
index 000000000..876709bfe
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/ios/Diagnostic_Microphone.h
@@ -0,0 +1,21 @@
+/*
+ * Diagnostic_Microphone.h
+ * Diagnostic Plugin - Microphone Module
+ *
+ * Copyright (c) 2018 Working Edge Ltd.
+ * Copyright (c) 2012 AVANTIC ESTUDIO DE INGENIEROS
+ */
+
+#import
+#import
+#import "Diagnostic.h"
+
+#import
+
+@interface Diagnostic_Microphone : CDVPlugin
+
+- (void) isMicrophoneAuthorized: (CDVInvokedUrlCommand*)command;
+- (void) getMicrophoneAuthorizationStatus: (CDVInvokedUrlCommand*)command;
+- (void) requestMicrophoneAuthorization: (CDVInvokedUrlCommand*)command;
+
+@end
diff --git a/cordova-plugin-moodleapp/src/ios/Diagnostic_Microphone.m b/cordova-plugin-moodleapp/src/ios/Diagnostic_Microphone.m
new file mode 100644
index 000000000..3d5e2f116
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/ios/Diagnostic_Microphone.m
@@ -0,0 +1,97 @@
+/*
+ * Diagnostic_Microphone.m
+ * Diagnostic Plugin - Microphone Module
+ *
+ * Copyright (c) 2018 Working Edge Ltd.
+ * Copyright (c) 2012 AVANTIC ESTUDIO DE INGENIEROS
+ */
+
+#import "Diagnostic_Microphone.h"
+
+@implementation Diagnostic_Microphone
+
+// Internal reference to Diagnostic singleton instance
+static Diagnostic* diagnostic;
+
+// Internal constants
+static NSString*const LOG_TAG = @"Diagnostic_Microphone[native]";
+
+- (void)pluginInitialize {
+
+ [super pluginInitialize];
+
+ diagnostic = [Diagnostic getInstance];
+}
+
+/********************************/
+#pragma mark - Plugin API
+/********************************/
+
+- (void) isMicrophoneAuthorized: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ CDVPluginResult* pluginResult;
+ @try {
+ AVAudioSessionRecordPermission recordPermission = [AVAudioSession sharedInstance].recordPermission;
+
+ if(recordPermission == AVAudioSessionRecordPermissionGranted) {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:1];
+ }
+ else {
+ pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:0];
+ }
+ [diagnostic sendPluginResultBool:recordPermission == AVAudioSessionRecordPermissionGranted :command];
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ };
+ }];
+}
+
+- (void) getMicrophoneAuthorizationStatus: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ NSString* status;
+ AVAudioSessionRecordPermission recordPermission = [AVAudioSession sharedInstance].recordPermission;
+ switch(recordPermission){
+ case AVAudioSessionRecordPermissionDenied:
+ status = AUTHORIZATION_DENIED;
+ break;
+ case AVAudioSessionRecordPermissionGranted:
+ status = AUTHORIZATION_GRANTED;
+ break;
+ case AVAudioSessionRecordPermissionUndetermined:
+ status = AUTHORIZATION_NOT_DETERMINED;
+ break;
+ }
+
+ [diagnostic logDebug:[NSString stringWithFormat:@"Microphone authorization status is: %@", status]];
+ [diagnostic sendPluginResultString:status:command];
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+- (void) requestMicrophoneAuthorization: (CDVInvokedUrlCommand*)command
+{
+ [self.commandDelegate runInBackground:^{
+ @try {
+ [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
+ [diagnostic logDebug:[NSString stringWithFormat:@"Has access to microphone: %d", granted]];
+ [diagnostic sendPluginResultBool:granted :command];
+ }];
+ }
+ @catch (NSException *exception) {
+ [diagnostic handlePluginException:exception :command];
+ }
+ }];
+}
+
+/********************************/
+#pragma mark - Internals
+/********************************/
+
+@end
diff --git a/cordova-plugin-moodleapp/src/ts/index.ts b/cordova-plugin-moodleapp/src/ts/index.ts
index 79542caa0..1b2ff1e8c 100644
--- a/cordova-plugin-moodleapp/src/ts/index.ts
+++ b/cordova-plugin-moodleapp/src/ts/index.ts
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import { Diagnostic } from './plugins/Diagnostic';
import { InstallReferrer } from './plugins/InstallReferrer';
import { SecureStorage } from './plugins/SecureStorage';
const api: MoodleAppPlugins = {
secureStorage: new SecureStorage(),
installReferrer: new InstallReferrer(),
+ diagnostic: new Diagnostic(),
};
// This is necessary to work around the default transpilation behavior,
diff --git a/cordova-plugin-moodleapp/src/ts/plugins/Diagnostic.ts b/cordova-plugin-moodleapp/src/ts/plugins/Diagnostic.ts
new file mode 100644
index 000000000..34c17739d
--- /dev/null
+++ b/cordova-plugin-moodleapp/src/ts/plugins/Diagnostic.ts
@@ -0,0 +1,131 @@
+// (C) Copyright 2015 Moodle Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// (C) Copyright 2015 Moodle Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// (C) Copyright 2015 Moodle Pty Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * Checks whether device hardware features are enabled or available to the app, e.g. camera, GPS, wifi
+ */
+export class Diagnostic {
+
+ /**
+ * Constants for requesting and reporting the various permission states.
+ */
+ declare permissionStatus: typeof permissionStatus;
+
+ /**
+ * ANDROID ONLY
+ * "Dangerous" permissions that need to be requested at run-time (Android 6.0/API 23 and above)
+ * See http://developer.android.com/guide/topics/security/permissions.html#perm-groups
+ *
+ */
+ declare permission: typeof permission;
+
+ constructor() {
+ this.permissionStatus = permissionStatus;
+ this.permission = permission;
+ }
+
+ isLocationEnabled(): Promise {
+ return new Promise((resolve, reject) => cordova.exec(resolve, reject, 'Diagnostic', 'isLocationEnabled'));
+ }
+
+ switchToLocationSettings(): Promise {
+ return new Promise((resolve, reject) => cordova.exec(resolve, reject, 'Diagnostic', 'switchToLocationSettings'));
+ }
+
+ switchToSettings(): Promise {
+ return new Promise((resolve, reject) => cordova.exec(resolve, reject, 'Diagnostic', 'switchToSettings'));
+ }
+
+ getLocationAuthorizationStatus(): Promise {
+ return new Promise((resolve, reject) =>
+ cordova.exec(resolve, reject, 'Diagnostic', 'getLocationAuthorizationStatus'));
+ }
+
+ requestLocationAuthorization(): Promise {
+ return new Promise((resolve, reject) => cordova.exec(resolve, reject, 'Diagnostic', 'requestLocationAuthorization'));
+ }
+
+ requestMicrophoneAuthorization(): Promise {
+ return new Promise((resolve, reject) =>
+ cordova.exec(resolve, reject, 'Diagnostic', 'requestMicrophoneAuthorization'));
+ }
+
+}
+
+const permission = {
+ acceptHandover: 'ACCEPT_HANDOVER',
+ accessBackgroundLocation: 'ACCESS_BACKGROUND_LOCATION',
+ accessCoarseLocation: 'ACCESS_COARSE_LOCATION',
+ accessFineLocation: 'ACCESS_FINE_LOCATION',
+ accessMediaLocation: 'ACCESS_MEDIA_LOCATION',
+ bodySensors: 'BODY_SENSORS',
+ bodySensorsBackground: 'BODY_SENSORS_BACKGROUND',
+ getAccounts: 'GET_ACCOUNTS',
+ readExternalStorage: 'READ_EXTERNAL_STORAGE',
+ readMediaAudio: 'READ_MEDIA_AUDIO',
+ readMediaImages: 'READ_MEDIA_IMAGES',
+ readMediaVideo: 'READ_MEDIA_VIDEO',
+ readPhoneState: 'READ_PHONE_STATE',
+ readSms: 'READ_SMS',
+ receiveMms: 'RECEIVE_MMS',
+ receiveSms: 'RECEIVE_SMS',
+ receiveWapPush: 'RECEIVE_WAP_PUSH',
+ recordAudio: 'RECORD_AUDIO',
+ sendSms: 'SEND_SMS',
+ useSip: 'USE_SIP',
+ uwbRanging: 'UWB_RANGING',
+ writeExternalStorage: 'WRITE_EXTERNAL_STORAGE',
+} as const;
+
+const permissionStatus = {
+ // Android only
+ deniedOnce: 'DENIED_ONCE',
+
+ // iOS only
+ restricted: 'restricted',
+ ephimeral: 'ephemeral',
+ provisional: 'provisional',
+
+ // Both iOS and Android
+ granted: 'authorized' || 'GRANTED',
+ grantedWhenInUse: 'authorized_when_in_use',
+ notRequested: 'not_determined' || 'NOT_REQUESTED',
+ deniedAlways: 'denied_always' || 'DENIED_ALWAYS',
+} as const;
diff --git a/cordova-plugin-moodleapp/types/index.d.ts b/cordova-plugin-moodleapp/types/index.d.ts
index 9e7c3c6f2..18482e2d5 100644
--- a/cordova-plugin-moodleapp/types/index.d.ts
+++ b/cordova-plugin-moodleapp/types/index.d.ts
@@ -14,12 +14,14 @@
import { InstallReferrer } from '../src/ts/plugins/InstallReferrer';
import { SecureStorage as SecureStorageImpl } from '../src/ts/plugins/SecureStorage';
+import { Diagnostic } from '../src/ts/plugins/Diagnostic';
declare global {
interface MoodleAppPlugins {
secureStorage: SecureStorageImpl;
installReferrer: InstallReferrer;
+ diagnostic: Diagnostic;
}
interface Cordova {
diff --git a/package-lock.json b/package-lock.json
index 41ee2d719..5566d6bc7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -23,7 +23,6 @@
"@awesome-cordova-plugins/clipboard": "^6.6.0",
"@awesome-cordova-plugins/core": "^6.6.0",
"@awesome-cordova-plugins/device": "^6.6.0",
- "@awesome-cordova-plugins/diagnostic": "^6.6.0",
"@awesome-cordova-plugins/file": "^6.6.0",
"@awesome-cordova-plugins/file-opener": "^6.6.0",
"@awesome-cordova-plugins/geolocation": "^6.6.0",
@@ -80,7 +79,6 @@
"cordova-plugin-wkuserscript": "^1.0.1",
"cordova-plugin-wkwebview-cookies": "^1.0.1",
"cordova-sqlite-storage": "^6.1.0",
- "cordova.plugins.diagnostic": "^7.1.4",
"core-js": "^3.9.1",
"es6-promise-plugin": "^4.2.2",
"ionicons": "^7.0.0",
@@ -2573,22 +2571,6 @@
"resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-11.0.3.tgz",
"integrity": "sha512-kyuRQ40/NWQVhqGIHq78Ehu2Bf9Mlg0LhmSmis6ZFJK7z933FRfYi8tHe/k/0fB+PGfCf95rJC6TO7dopaFvAg=="
},
- "node_modules/@awesome-cordova-plugins/diagnostic": {
- "version": "6.6.0",
- "license": "MIT",
- "dependencies": {
- "@types/cordova": "latest"
- },
- "peerDependencies": {
- "@awesome-cordova-plugins/core": "^6.0.1",
- "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
- }
- },
- "node_modules/@awesome-cordova-plugins/diagnostic/node_modules/@types/cordova": {
- "version": "11.0.3",
- "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-11.0.3.tgz",
- "integrity": "sha512-kyuRQ40/NWQVhqGIHq78Ehu2Bf9Mlg0LhmSmis6ZFJK7z933FRfYi8tHe/k/0fB+PGfCf95rJC6TO7dopaFvAg=="
- },
"node_modules/@awesome-cordova-plugins/file": {
"version": "6.6.0",
"license": "MIT",
@@ -11904,13 +11886,6 @@
"version": "2.0.20",
"license": "MIT"
},
- "node_modules/colors": {
- "version": "1.4.0",
- "license": "MIT",
- "engines": {
- "node": ">=0.1.90"
- }
- },
"node_modules/combined-stream": {
"version": "1.0.8",
"license": "MIT",
@@ -13003,20 +12978,6 @@
"version": "4.1.0",
"license": "MIT"
},
- "node_modules/cordova.plugins.diagnostic": {
- "version": "7.1.4",
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "colors": "1.4.0",
- "elementtree": "^0.1.6",
- "minimist": "1.2.6"
- }
- },
- "node_modules/cordova.plugins.diagnostic/node_modules/minimist": {
- "version": "1.2.6",
- "license": "MIT"
- },
"node_modules/cordova/node_modules/fs-extra": {
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz",
diff --git a/package.json b/package.json
index 440f6af04..cea8eeef2 100644
--- a/package.json
+++ b/package.json
@@ -58,7 +58,6 @@
"@awesome-cordova-plugins/clipboard": "^6.6.0",
"@awesome-cordova-plugins/core": "^6.6.0",
"@awesome-cordova-plugins/device": "^6.6.0",
- "@awesome-cordova-plugins/diagnostic": "^6.6.0",
"@awesome-cordova-plugins/file": "^6.6.0",
"@awesome-cordova-plugins/file-opener": "^6.6.0",
"@awesome-cordova-plugins/geolocation": "^6.6.0",
@@ -114,7 +113,6 @@
"cordova-plugin-wkuserscript": "^1.0.1",
"cordova-plugin-wkwebview-cookies": "^1.0.1",
"cordova-sqlite-storage": "^6.1.0",
- "cordova.plugins.diagnostic": "^7.1.4",
"core-js": "^3.9.1",
"es6-promise-plugin": "^4.2.2",
"ionicons": "^7.0.0",
@@ -236,11 +234,6 @@
"cordova-plugin-wkuserscript": {},
"cordova-plugin-wkwebview-cookies": {},
"cordova-sqlite-storage": {},
- "cordova.plugins.diagnostic": {
- "ANDROID_SUPPORT_VERSION": "28.+",
- "ANDROIDX_VERSION": "1.0.0",
- "ANDROIDX_APPCOMPAT_VERSION": "1.6.1"
- },
"nl.kingsquare.cordova.background-audio": {}
}
}
diff --git a/src/core/features/fileuploader/components/audio-recorder/audio-recorder.component.ts b/src/core/features/fileuploader/components/audio-recorder/audio-recorder.component.ts
index 6c8eba582..2c2a3187f 100644
--- a/src/core/features/fileuploader/components/audio-recorder/audio-recorder.component.ts
+++ b/src/core/features/fileuploader/components/audio-recorder/audio-recorder.component.ts
@@ -15,7 +15,7 @@
import { ChangeDetectionStrategy, Component, ElementRef, OnDestroy } from '@angular/core';
import { CoreModalComponent } from '@classes/modal-component';
import { CorePlatform } from '@services/platform';
-import { Diagnostic, DomSanitizer, Translate } from '@singletons';
+import { DomSanitizer, Translate } from '@singletons';
import { BehaviorSubject, combineLatest, Observable, OperatorFunction } from 'rxjs';
import { Mp3MediaRecorder } from 'mp3-mediarecorder';
import { map, shareReplay, tap } from 'rxjs/operators';
@@ -26,6 +26,7 @@ import { CAPTURE_ERROR_NO_MEDIA_FILES, CoreCaptureError } from '@classes/errors/
import { CoreFileUploaderAudioRecording } from '@features/fileuploader/services/fileuploader';
import { CoreFile, CoreFileProvider } from '@services/file';
import { CorePath } from '@singletons/path';
+import { CoreNative } from '@features/native/services/native';
@Component({
selector: 'core-fileuploader-audio-recorder',
@@ -204,17 +205,19 @@ export class CoreFileUploaderAudioRecorderComponent extends CoreModalComponent {
- if (!CorePlatform.isMobile()) {
+ const diagnostic = await CoreNative.plugin('diagnostic')?.getInstance();
+
+ if (!CorePlatform.isMobile() || !diagnostic) {
return;
}
- const status = await Diagnostic.requestMicrophoneAuthorization();
+ const status = await diagnostic.requestMicrophoneAuthorization();
switch (status) {
- case Diagnostic.permissionStatus.DENIED_ONCE:
- case Diagnostic.permissionStatus.DENIED_ALWAYS:
+ case diagnostic.permissionStatus.deniedOnce:
+ case diagnostic.permissionStatus.deniedAlways:
throw new Error(Translate.instant('core.fileuploader.microphonepermissiondenied'));
- case Diagnostic.permissionStatus.RESTRICTED:
+ case diagnostic.permissionStatus.restricted:
throw new Error(Translate.instant('core.fileuploader.microphonepermissionrestricted'));
}
}
diff --git a/src/core/features/native/native.module.ts b/src/core/features/native/native.module.ts
index 102e004f4..39ab66004 100644
--- a/src/core/features/native/native.module.ts
+++ b/src/core/features/native/native.module.ts
@@ -19,7 +19,6 @@ import { Camera } from '@awesome-cordova-plugins/camera/ngx';
import { Chooser } from '@features/native/plugins/chooser';
import { Clipboard } from '@awesome-cordova-plugins/clipboard/ngx';
import { Device } from '@awesome-cordova-plugins/device/ngx';
-import { Diagnostic } from '@awesome-cordova-plugins/diagnostic/ngx';
import { File } from '@awesome-cordova-plugins/file/ngx';
import { FileOpener } from '@awesome-cordova-plugins/file-opener/ngx';
import { Geolocation } from '@awesome-cordova-plugins/geolocation/ngx';
@@ -43,7 +42,6 @@ export const CORE_NATIVE_SERVICES = [
Chooser,
Clipboard,
Device,
- Diagnostic,
File,
FileOpener,
Geolocation,
@@ -68,7 +66,6 @@ export const CORE_NATIVE_SERVICES = [
Camera,
Clipboard,
Device,
- Diagnostic,
File,
FileOpener,
Geolocation,
diff --git a/src/core/features/settings/pages/general/general.ts b/src/core/features/settings/pages/general/general.ts
index 638b088ef..176a04772 100644
--- a/src/core/features/settings/pages/general/general.ts
+++ b/src/core/features/settings/pages/general/general.ts
@@ -20,13 +20,14 @@ import { CoreLang } from '@services/lang';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreSettingsHelper, CoreColorScheme, CoreZoomLevel } from '../../services/settings-helper';
import { CoreIframeUtils } from '@services/utils/iframe';
-import { Diagnostic, Translate } from '@singletons';
+import { Translate } from '@singletons';
import { CoreSites } from '@services/sites';
import { CoreUtils } from '@services/utils/utils';
import { AlertButton } from '@ionic/angular';
import { CoreNavigator } from '@services/navigator';
import { CorePlatform } from '@services/platform';
import { CoreAnalytics } from '@services/analytics';
+import { CoreNative } from '@features/native/services/native';
/**
* Page that displays the general settings.
@@ -263,7 +264,7 @@ export class CoreSettingsGeneralPage {
ev.stopPropagation();
ev.preventDefault();
- Diagnostic.switchToSettings();
+ CoreNative.plugin('diagnostic')?.switchToSettings();
}
}
diff --git a/src/core/services/geolocation.ts b/src/core/services/geolocation.ts
index c696550f3..2f3df421b 100644
--- a/src/core/services/geolocation.ts
+++ b/src/core/services/geolocation.ts
@@ -17,12 +17,13 @@ import { Coordinates } from '@awesome-cordova-plugins/geolocation';
import { CoreApp } from '@services/app';
import { CoreAnyError, CoreError } from '@classes/errors/error';
-import { Geolocation, Diagnostic, makeSingleton } from '@singletons';
+import { Geolocation, makeSingleton } from '@singletons';
import { CoreUtils } from './utils/utils';
import { CorePlatform } from './platform';
import { CoreSilentError } from '@classes/errors/silenterror';
import { CoreSubscriptions } from '@singletons/subscriptions';
import { CoreLogger } from '@singletons/logger';
+import { CoreNative } from '@features/native/services/native';
@Injectable({ providedIn: 'root' })
export class CoreGeolocationProvider {
@@ -79,7 +80,13 @@ export class CoreGeolocationProvider {
* @throws {CoreGeolocationError}
*/
async enableLocation(): Promise {
- let locationEnabled = await Diagnostic.isLocationEnabled();
+ const diagnostic = CoreNative.plugin('diagnostic');
+
+ if (!diagnostic) {
+ return;
+ }
+
+ let locationEnabled = await diagnostic.isLocationEnabled();
if (locationEnabled) {
// Location is enabled.
@@ -87,10 +94,10 @@ export class CoreGeolocationProvider {
}
if (!CorePlatform.isIOS()) {
- Diagnostic.switchToLocationSettings();
+ diagnostic.switchToLocationSettings();
await CoreApp.waitForResume(30000);
- locationEnabled = await Diagnostic.isLocationEnabled();
+ locationEnabled = await diagnostic.isLocationEnabled();
}
if (!locationEnabled) {
@@ -105,26 +112,34 @@ export class CoreGeolocationProvider {
* @throws {CoreGeolocationError}
*/
protected async doAuthorizeLocation(failOnDeniedOnce: boolean = false): Promise {
- const authorizationStatus = await Diagnostic.getLocationAuthorizationStatus();
+ const diagnostic = await CoreNative.plugin('diagnostic')?.getInstance();
+
+ if (!diagnostic) {
+ return;
+ }
+
+ const authorizationStatus = await diagnostic.getLocationAuthorizationStatus();
this.logger.log(`Authorize location: status ${authorizationStatus}`);
switch (authorizationStatus) {
- case Diagnostic.permissionStatus.DENIED_ONCE:
+ case diagnostic.permissionStatus.deniedOnce:
if (failOnDeniedOnce) {
throw new CoreGeolocationError(CoreGeolocationErrorReason.PERMISSION_DENIED);
}
+
+ case diagnostic.permissionStatus.granted:
+ case diagnostic.permissionStatus.grantedWhenInUse:
+ // Location is authorized.
+ return;
+
// Fall through.
- case Diagnostic.permissionStatus.NOT_REQUESTED:
+ case diagnostic.permissionStatus.notRequested:
this.logger.log('Request location authorization.');
await this.requestLocationAuthorization();
this.logger.log('Location authorization granted.');
await CoreApp.waitForResume(500);
await this.doAuthorizeLocation(true);
- return;
- case Diagnostic.permissionStatus.GRANTED:
- case Diagnostic.permissionStatus.GRANTED_WHEN_IN_USE:
- // Location is authorized.
return;
default:
throw new CoreGeolocationError(CoreGeolocationErrorReason.PERMISSION_DENIED);
@@ -151,7 +166,13 @@ export class CoreGeolocationProvider {
* @returns If location can be requested.
*/
async canRequest(): Promise {
- return CoreUtils.promiseWorks(Diagnostic.getLocationAuthorizationStatus());
+ const diagnostic = CoreNative.plugin('diagnostic');
+
+ if (diagnostic) {
+ return CoreUtils.promiseWorks(diagnostic.getLocationAuthorizationStatus());
+ }
+
+ return false;
}
/**
@@ -159,7 +180,7 @@ export class CoreGeolocationProvider {
*/
protected async requestLocationAuthorization(): Promise {
if (!CorePlatform.isIOS()) {
- await Diagnostic.requestLocationAuthorization();
+ await CoreNative.plugin('diagnostic')?.requestLocationAuthorization();
return;
}
@@ -168,7 +189,8 @@ export class CoreGeolocationProvider {
return new Promise((resolve, reject) => {
// Don't display an error if app is sent to the background, just finish the process.
const unsubscribe = CoreSubscriptions.once(CorePlatform.pause, () => reject(new CoreSilentError()));
- Diagnostic.requestLocationAuthorization().then(() => resolve(), reject).finally(() => unsubscribe());
+ CoreNative.plugin('diagnostic')?.requestLocationAuthorization()
+ .then(() => resolve(), reject).finally(() => unsubscribe());
});
}
diff --git a/src/core/services/utils/iframe.ts b/src/core/services/utils/iframe.ts
index 0410a2989..6837b2625 100644
--- a/src/core/services/utils/iframe.ts
+++ b/src/core/services/utils/iframe.ts
@@ -24,7 +24,7 @@ import { CoreDomUtils } from '@services/utils/dom';
import { CoreUrlUtils } from '@services/utils/url';
import { CoreUtils } from '@services/utils/utils';
-import { makeSingleton, NgZone, Translate, Diagnostic } from '@singletons';
+import { makeSingleton, NgZone, Translate } from '@singletons';
import { CoreLogger } from '@singletons/logger';
import { CoreUrl } from '@singletons/url';
import { CoreWindow } from '@singletons/window';
@@ -36,6 +36,7 @@ import { FrameElement } from '@classes/element-controllers/FrameElementControlle
import { CoreMimetypeUtils } from './mimetype';
import { CoreFilepool } from '@services/filepool';
import { CoreSite } from '@classes/sites/site';
+import { CoreNative } from '@features/native/services/native';
type CoreFrameElement = FrameElement & {
window?: Window;
@@ -627,7 +628,7 @@ export class CoreIframeUtilsProvider {
{
text: Translate.instant('core.opensettings'),
handler: (): void => {
- Diagnostic.switchToSettings();
+ CoreNative.plugin('diagnostic')?.switchToSettings();
},
},
],
diff --git a/src/core/singletons/index.ts b/src/core/singletons/index.ts
index 1bef7dd7d..eaba8d516 100644
--- a/src/core/singletons/index.ts
+++ b/src/core/singletons/index.ts
@@ -40,7 +40,6 @@ import {
import { Badge as BadgeService } from '@awesome-cordova-plugins/badge/ngx';
import { Camera as CameraService } from '@awesome-cordova-plugins/camera/ngx';
import { Clipboard as ClipboardService } from '@awesome-cordova-plugins/clipboard/ngx';
-import { Diagnostic as DiagnosticService } from '@awesome-cordova-plugins/diagnostic/ngx';
import { Device as DeviceService } from '@awesome-cordova-plugins/device/ngx';
import { File as FileService } from '@awesome-cordova-plugins/file/ngx';
import { FileOpener as FileOpenerService } from '@awesome-cordova-plugins/file-opener/ngx';
@@ -171,7 +170,6 @@ export function makeSingleton(
// Convert ionic-native services to singleton.
export const Badge = makeSingleton(BadgeService);
export const Clipboard = makeSingleton(ClipboardService);
-export const Diagnostic = makeSingleton(DiagnosticService);
export const File = makeSingleton(FileService);
export const FileOpener = makeSingleton(FileOpenerService);
export const Geolocation = makeSingleton(GeolocationService);