Skip to content

Commit 44a0cea

Browse files
MOBILE-4501 diagnostic: Include diagnostic plugin
Co-Authored-By: Dave Alden <[email protected]>
1 parent 0c7e7c9 commit 44a0cea

File tree

8 files changed

+2268
-0
lines changed

8 files changed

+2268
-0
lines changed

cordova-plugin-moodleapp/src/android/Diagnostic.java

Lines changed: 1073 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 318 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,318 @@
1+
// (C) Copyright 2015 Moodle Pty Ltd.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.moodle.moodlemobile;
16+
17+
/*
18+
* Imports
19+
*/
20+
import org.apache.cordova.CordovaWebView;
21+
import org.apache.cordova.CallbackContext;
22+
import org.apache.cordova.CordovaPlugin;
23+
import org.apache.cordova.CordovaInterface;
24+
import org.apache.cordova.PluginResult;
25+
import org.json.JSONArray;
26+
import org.json.JSONException;
27+
28+
import android.content.BroadcastReceiver;
29+
import android.content.IntentFilter;
30+
import android.location.LocationManager;
31+
import android.os.Build;
32+
import android.util.Log;
33+
34+
import android.content.Context;
35+
import android.content.Intent;
36+
import android.provider.Settings;
37+
38+
/**
39+
* Diagnostic plugin implementation for Android
40+
*/
41+
public class Diagnostic_Location extends CordovaPlugin{
42+
43+
44+
/*************
45+
* Constants *
46+
*************/
47+
48+
/**
49+
* Tag for debug log messages
50+
*/
51+
public static final String TAG = "Diagnostic_Location";
52+
53+
private static String gpsLocationPermission = "ACCESS_FINE_LOCATION";
54+
private static String networkLocationPermission = "ACCESS_COARSE_LOCATION";
55+
private static String backgroundLocationPermission = "ACCESS_BACKGROUND_LOCATION";
56+
57+
58+
private static final String LOCATION_MODE_HIGH_ACCURACY = "high_accuracy";
59+
private static final String LOCATION_MODE_DEVICE_ONLY = "device_only";
60+
private static final String LOCATION_MODE_BATTERY_SAVING = "battery_saving";
61+
private static final String LOCATION_MODE_OFF = "location_off";
62+
private static final String LOCATION_MODE_UNKNOWN = "unknown";
63+
64+
/*************
65+
* Variables *
66+
*************/
67+
68+
/**
69+
* Singleton class instance
70+
*/
71+
public static Diagnostic_Location instance = null;
72+
73+
private Diagnostic diagnostic;
74+
75+
public static LocationManager locationManager;
76+
77+
/**
78+
* Current Cordova callback context (on this thread)
79+
*/
80+
protected CallbackContext currentContext;
81+
82+
private String currentLocationMode = null;
83+
84+
/*************
85+
* Public API
86+
************/
87+
88+
/**
89+
* Constructor.
90+
*/
91+
public Diagnostic_Location() {}
92+
93+
/**
94+
* Sets the context of the Command. This can then be used to do things like
95+
* get file paths associated with the Activity.
96+
*
97+
* @param cordova The context of the main Activity.
98+
* @param webView The CordovaWebView Cordova is running in.
99+
*/
100+
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
101+
Log.d(TAG, "initialize()");
102+
instance = this;
103+
diagnostic = Diagnostic.getInstance();
104+
105+
try {
106+
diagnostic.applicationContext.registerReceiver(locationProviderChangedReceiver, new IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION));
107+
locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
108+
}catch(Exception e){
109+
diagnostic.logWarning("Unable to register Location Provider Change receiver: " + e.getMessage());
110+
}
111+
112+
try {
113+
currentLocationMode = getLocationModeName();
114+
}catch(Exception e){
115+
diagnostic.logWarning("Unable to get initial location mode: " + e.getMessage());
116+
}
117+
118+
super.initialize(cordova, webView);
119+
}
120+
121+
/**
122+
* Called on destroying activity
123+
*/
124+
public void onDestroy() {
125+
try {
126+
diagnostic.applicationContext.unregisterReceiver(locationProviderChangedReceiver);
127+
}catch(Exception e){
128+
diagnostic.logWarning("Unable to unregister Location Provider Change receiver: " + e.getMessage());
129+
}
130+
}
131+
132+
/**
133+
* Executes the request and returns PluginResult.
134+
*
135+
* @param action The action to execute.
136+
* @param args JSONArry of arguments for the plugin.
137+
* @param callbackContext The callback id used when calling back into JavaScript.
138+
* @return True if the action was valid, false if not.
139+
*/
140+
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
141+
Diagnostic.instance.currentContext = currentContext = callbackContext;
142+
143+
try {
144+
if (action.equals("switchToLocationSettings")){
145+
switchToLocationSettings();
146+
callbackContext.success();
147+
} else if(action.equals("isLocationAvailable")) {
148+
callbackContext.success(isGpsLocationAvailable() || isNetworkLocationAvailable() ? 1 : 0);
149+
} else if(action.equals("isLocationEnabled")) {
150+
callbackContext.success(isGpsLocationEnabled() || isNetworkLocationEnabled() ? 1 : 0);
151+
} else if(action.equals("isGpsLocationAvailable")) {
152+
callbackContext.success(isGpsLocationAvailable() ? 1 : 0);
153+
} else if(action.equals("isNetworkLocationAvailable")) {
154+
callbackContext.success(isNetworkLocationAvailable() ? 1 : 0);
155+
} else if(action.equals("isGpsLocationEnabled")) {
156+
callbackContext.success(isGpsLocationEnabled() ? 1 : 0);
157+
} else if(action.equals("isNetworkLocationEnabled")) {
158+
callbackContext.success(isNetworkLocationEnabled() ? 1 : 0);
159+
} else if(action.equals("getLocationMode")) {
160+
callbackContext.success(getLocationModeName());
161+
} else if(action.equals("requestLocationAuthorization")) {
162+
requestLocationAuthorization(args, callbackContext);
163+
}else {
164+
diagnostic.handleError("Invalid action");
165+
return false;
166+
}
167+
}catch(Exception e ) {
168+
diagnostic.handleError("Exception occurred: ".concat(e.getMessage()));
169+
return false;
170+
}
171+
return true;
172+
}
173+
174+
public boolean isGpsLocationAvailable() throws Exception {
175+
boolean result = isGpsLocationEnabled() && isLocationAuthorized();
176+
diagnostic.logDebug("GPS location available: " + result);
177+
return result;
178+
}
179+
180+
public boolean isGpsLocationEnabled() throws Exception {
181+
int mode = getLocationMode();
182+
boolean result = (mode == 3 || mode == 1);
183+
diagnostic.logDebug("GPS location setting enabled: " + result);
184+
return result;
185+
}
186+
187+
public boolean isNetworkLocationAvailable() throws Exception {
188+
boolean result = isNetworkLocationEnabled() && isLocationAuthorized();
189+
diagnostic.logDebug("Network location available: " + result);
190+
return result;
191+
}
192+
193+
public boolean isNetworkLocationEnabled() throws Exception {
194+
int mode = getLocationMode();
195+
boolean result = (mode == 3 || mode == 2);
196+
diagnostic.logDebug("Network location setting enabled: " + result);
197+
return result;
198+
}
199+
200+
public String getLocationModeName() throws Exception {
201+
String modeName;
202+
int mode = getLocationMode();
203+
switch(mode){
204+
case Settings.Secure.LOCATION_MODE_HIGH_ACCURACY:
205+
modeName = LOCATION_MODE_HIGH_ACCURACY;
206+
break;
207+
case Settings.Secure.LOCATION_MODE_SENSORS_ONLY:
208+
modeName = LOCATION_MODE_DEVICE_ONLY;
209+
break;
210+
case Settings.Secure.LOCATION_MODE_BATTERY_SAVING:
211+
modeName = LOCATION_MODE_BATTERY_SAVING;
212+
break;
213+
case Settings.Secure.LOCATION_MODE_OFF:
214+
modeName = LOCATION_MODE_OFF;
215+
break;
216+
default:
217+
modeName = LOCATION_MODE_UNKNOWN;
218+
}
219+
return modeName;
220+
}
221+
222+
public void notifyLocationStateChange(){
223+
try {
224+
String newMode = getLocationModeName();
225+
if(!newMode.equals(currentLocationMode)){
226+
diagnostic.logDebug("Location mode change to: " + newMode);
227+
diagnostic.executePluginJavascript("location._onLocationStateChange(\"" + newMode +"\");");
228+
currentLocationMode = newMode;
229+
}
230+
}catch(Exception e){
231+
diagnostic.logError("Error retrieving current location mode on location state change: "+e.toString());
232+
}
233+
}
234+
235+
public void switchToLocationSettings() {
236+
diagnostic.logDebug("Switch to Location Settings");
237+
Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
238+
cordova.getActivity().startActivity(settingsIntent);
239+
}
240+
241+
public void requestLocationAuthorization(JSONArray args, CallbackContext callbackContext) throws Exception{
242+
JSONArray permissionsToRequest = new JSONArray();
243+
boolean shouldRequestBackground = args.getBoolean(0);
244+
boolean shouldRequestPrecise = args.getBoolean(1);
245+
246+
permissionsToRequest.put(networkLocationPermission);
247+
if(shouldRequestPrecise || Build.VERSION.SDK_INT < 31){
248+
permissionsToRequest.put(gpsLocationPermission);
249+
}
250+
251+
if(shouldRequestBackground && Build.VERSION.SDK_INT >= 29 ){
252+
permissionsToRequest.put(backgroundLocationPermission);
253+
}
254+
255+
int requestId = Diagnostic.instance.storeContextByRequestId(callbackContext);
256+
Diagnostic.instance._requestRuntimePermissions(permissionsToRequest, requestId);
257+
258+
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
259+
result.setKeepCallback(true);
260+
callbackContext.sendPluginResult(result);
261+
}
262+
263+
264+
265+
/************
266+
* Internals
267+
***********/
268+
/**
269+
* Returns current location mode
270+
*/
271+
private int getLocationMode() throws Exception {
272+
int mode;
273+
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)
274+
mode = Settings.Secure.getInt(this.cordova.getActivity().getContentResolver(), Settings.Secure.LOCATION_MODE);
275+
}else{ // Pre-Kitkat and post-Oreo
276+
if(isLocationProviderEnabled(LocationManager.GPS_PROVIDER) && isLocationProviderEnabled(LocationManager.NETWORK_PROVIDER)){
277+
mode = 3;
278+
} else if(isLocationProviderEnabled(LocationManager.GPS_PROVIDER)){
279+
mode = 1;
280+
} else if(isLocationProviderEnabled(LocationManager.NETWORK_PROVIDER)){
281+
mode = 2;
282+
}else{
283+
mode = 0;
284+
}
285+
}
286+
return mode;
287+
}
288+
289+
private boolean isLocationAuthorized() throws Exception {
290+
boolean authorized = diagnostic.hasRuntimePermission(diagnostic.permissionsMap.get(gpsLocationPermission)) || diagnostic.hasRuntimePermission(diagnostic.permissionsMap.get(networkLocationPermission));
291+
Log.v(TAG, "Location permission is "+(authorized ? "authorized" : "unauthorized"));
292+
return authorized;
293+
}
294+
295+
private boolean isLocationProviderEnabled(String provider) {
296+
return locationManager.isProviderEnabled(provider);
297+
}
298+
299+
300+
/************
301+
* Overrides
302+
***********/
303+
304+
protected final BroadcastReceiver locationProviderChangedReceiver = new BroadcastReceiver() {
305+
@Override
306+
public void onReceive(Context context, Intent intent) {
307+
try {
308+
final String action = intent.getAction();
309+
if(instance != null && action.equals(LocationManager.PROVIDERS_CHANGED_ACTION)){
310+
Log.v(TAG, "onReceiveLocationProviderChange");
311+
instance.notifyLocationStateChange();
312+
}
313+
} catch (Exception e) {
314+
diagnostic.logError("Error receiving location provider state change: "+e.toString());
315+
}
316+
}
317+
};
318+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Diagnostic.h
3+
* Diagnostic Plugin - Core Module
4+
*
5+
* Copyright (c) 2015 Working Edge Ltd.
6+
* Copyright (c) 2012 AVANTIC ESTUDIO DE INGENIEROS
7+
*/
8+
9+
#import <Cordova/CDV.h>
10+
#import <Cordova/CDVPlugin.h>
11+
#import <WebKit/WebKit.h>
12+
13+
#import <mach/machine.h>
14+
#import <sys/types.h>
15+
#import <sys/sysctl.h>
16+
17+
// Public constants
18+
extern NSString*const UNKNOWN;
19+
20+
extern NSString*const AUTHORIZATION_NOT_DETERMINED;
21+
extern NSString*const AUTHORIZATION_DENIED;
22+
extern NSString*const AUTHORIZATION_GRANTED;
23+
extern NSString*const AUTHORIZATION_PROVISIONAL;
24+
extern NSString*const AUTHORIZATION_EPHEMERAL;
25+
extern NSString*const AUTHORIZATION_LIMITED;
26+
27+
@interface Diagnostic : CDVPlugin
28+
29+
@property (nonatomic) float osVersion;
30+
@property (nonatomic) BOOL debugEnabled;
31+
32+
// Plugin API
33+
- (void) enableDebug: (CDVInvokedUrlCommand*)command;
34+
- (void) switchToSettings: (CDVInvokedUrlCommand*)command;
35+
- (void) getBackgroundRefreshStatus: (CDVInvokedUrlCommand*)command;
36+
- (void) getArchitecture: (CDVInvokedUrlCommand*)command;
37+
- (void) getCurrentBatteryLevel: (CDVInvokedUrlCommand*)command;
38+
- (void) getDeviceOSVersion: (CDVInvokedUrlCommand*)command;
39+
- (void) getBuildOSVersion: (CDVInvokedUrlCommand*)command;
40+
- (void) isMobileDataEnabled: (CDVInvokedUrlCommand*)command;
41+
42+
// Utilities
43+
+ (id) getInstance;
44+
- (void) sendPluginResult: (CDVPluginResult*)result :(CDVInvokedUrlCommand*)command;
45+
- (void) sendPluginResultSuccess:(CDVInvokedUrlCommand*)command;
46+
- (void) sendPluginNoResultAndKeepCallback:(CDVInvokedUrlCommand*)command;
47+
- (void) sendPluginResultBool: (BOOL)result :(CDVInvokedUrlCommand*)command;
48+
- (void) sendPluginResultString: (NSString*)result :(CDVInvokedUrlCommand*)command;
49+
- (void) sendPluginError: (NSString*) errorMessage :(CDVInvokedUrlCommand*)command;
50+
- (void) handlePluginException: (NSException*) exception :(CDVInvokedUrlCommand*)command;
51+
- (void)executeGlobalJavascript: (NSString*)jsString;
52+
- (NSString*) arrayToJsonString:(NSArray*)inputArray;
53+
- (NSString*) objectToJsonString:(NSDictionary*)inputObject;
54+
- (NSArray*) jsonStringToArray:(NSString*)jsonStr;
55+
- (NSDictionary*) jsonStringToDictionary:(NSString*)jsonStr;
56+
- (bool)isNull: (NSString*)str;
57+
- (void)logDebug: (NSString*)msg;
58+
- (void)logError: (NSString*)msg;
59+
- (NSString*)escapeDoubleQuotes: (NSString*)str;
60+
- (void) setSetting: (NSString*)key forValue:(id)value;
61+
- (id) getSetting: (NSString*) key;
62+
63+
@end

0 commit comments

Comments
 (0)