react-native-radar 3.5.6 → 3.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,7 +18,7 @@ android {
18
18
  minSdkVersion 16
19
19
  targetSdkVersion 31
20
20
  versionCode 1
21
- versionName '3.5.6'
21
+ versionName '3.5.7'
22
22
  }
23
23
  lintOptions {
24
24
  abortOnError false
@@ -39,6 +39,7 @@ import org.json.JSONException;
39
39
  import org.json.JSONObject;
40
40
 
41
41
  import java.util.EnumSet;
42
+ import java.util.Map;
42
43
 
43
44
  public class RNRadarModule extends ReactContextBaseJavaModule implements PermissionListener {
44
45
 
@@ -78,16 +79,54 @@ public class RNRadarModule extends ReactContextBaseJavaModule implements Permiss
78
79
  Radar.setUserId(userId);
79
80
  }
80
81
 
82
+ @ReactMethod
83
+ public void getUserId(final Promise promise) {
84
+ if (promise == null) {
85
+ return;
86
+ }
87
+
88
+ promise.resolve(Radar.getUserId());
89
+ }
90
+
81
91
  @ReactMethod
82
92
  public void setDescription(String description) {
83
93
  Radar.setDescription(description);
84
94
  }
85
95
 
96
+ @ReactMethod
97
+ public void getDescription(final Promise promise) {
98
+ if (promise == null) {
99
+ return;
100
+ }
101
+
102
+ promise.resolve(Radar.getDescription());
103
+ }
104
+
86
105
  @ReactMethod
87
106
  public void setMetadata(ReadableMap metadataMap) throws JSONException {
88
107
  Radar.setMetadata(RNRadarUtils.jsonForMap(metadataMap));
89
108
  }
90
109
 
110
+ @ReactMethod
111
+ public void getMetadata(final Promise promise) throws JSONException {
112
+ if (promise == null) {
113
+ return;
114
+ }
115
+
116
+ JSONObject metaJson = Radar.getMetadata();
117
+ promise.resolve(RNRadarUtils.mapForJson(metaJson));
118
+ }
119
+
120
+ @ReactMethod
121
+ public void setAnonymousTrackingEnabled(boolean enabled) {
122
+ Radar.setAnonymousTrackingEnabled(enabled);
123
+ }
124
+
125
+ @ReactMethod
126
+ public void setAdIdEnabled(boolean enabled) {
127
+ Radar.setAdIdEnabled(enabled);
128
+ }
129
+
91
130
  @ReactMethod
92
131
  public void getPermissionsStatus(final Promise promise) {
93
132
  if (promise == null) {
@@ -146,8 +185,22 @@ public class RNRadarModule extends ReactContextBaseJavaModule implements Permiss
146
185
  }
147
186
 
148
187
  @ReactMethod
149
- public void getLocation(final Promise promise) {
150
- Radar.getLocation(new Radar.RadarLocationCallback() {
188
+ public void getLocation(String desiredAccuracy, final Promise promise) {
189
+
190
+ RadarTrackingOptions.RadarTrackingOptionsDesiredAccuracy accuracyLevel = RadarTrackingOptions.RadarTrackingOptionsDesiredAccuracy.MEDIUM;
191
+ String accuracy = desiredAccuracy != null ? desiredAccuracy.toLowerCase() : "medium";
192
+
193
+ if (accuracy.equals("low")) {
194
+ accuracyLevel = RadarTrackingOptions.RadarTrackingOptionsDesiredAccuracy.LOW;
195
+ } else if (accuracy.equals("medium")) {
196
+ accuracyLevel = RadarTrackingOptions.RadarTrackingOptionsDesiredAccuracy.MEDIUM;
197
+ } else if (accuracy.equals("high")) {
198
+ accuracyLevel = RadarTrackingOptions.RadarTrackingOptionsDesiredAccuracy.HIGH;
199
+ } else {
200
+ promise.reject(Radar.RadarStatus.ERROR_BAD_REQUEST.toString(), Radar.RadarStatus.ERROR_BAD_REQUEST.toString());
201
+ }
202
+
203
+ Radar.getLocation(accuracyLevel, new Radar.RadarLocationCallback() {
151
204
  @Override
152
205
  public void onComplete(@NonNull Radar.RadarStatus status, @Nullable Location location, boolean stopped) {
153
206
  if (promise == null) {
@@ -312,6 +365,29 @@ public class RNRadarModule extends ReactContextBaseJavaModule implements Permiss
312
365
  Radar.stopTracking();
313
366
  }
314
367
 
368
+ @ReactMethod
369
+ public void isTracking(final Promise promise) {
370
+ if (promise == null) {
371
+ return;
372
+ }
373
+
374
+ promise.resolve(Radar.isTracking());
375
+ }
376
+
377
+ @ReactMethod
378
+ public void getTrackingOptions(final Promise promise) {
379
+ if (promise == null) {
380
+ return;
381
+ }
382
+ try {
383
+ RadarTrackingOptions options = Radar.getTrackingOptions();
384
+ promise.resolve(RNRadarUtils.mapForJson(options.toJson()));
385
+ } catch(JSONException e) {
386
+ Log.e(TAG, "JSONException", e);
387
+ promise.reject(Radar.RadarStatus.ERROR_SERVER.toString(), Radar.RadarStatus.ERROR_SERVER.toString());
388
+ }
389
+ }
390
+
315
391
  @ReactMethod
316
392
  public void setForegroundServiceOptions(ReadableMap optionsMap) {
317
393
  try {
@@ -333,6 +409,20 @@ public class RNRadarModule extends ReactContextBaseJavaModule implements Permiss
333
409
  Radar.rejectEvent(eventId);
334
410
  }
335
411
 
412
+ @ReactMethod
413
+ public void getTripOptions(final Promise promise) {
414
+ if (promise == null) {
415
+ return;
416
+ }
417
+ try {
418
+ RadarTripOptions options = Radar.getTripOptions();
419
+ promise.resolve(options != null ? RNRadarUtils.mapForJson(options.toJson()) : null);
420
+ } catch(JSONException e) {
421
+ Log.e(TAG, "JSONException", e);
422
+ promise.reject(Radar.RadarStatus.ERROR_SERVER.toString(), Radar.RadarStatus.ERROR_SERVER.toString());
423
+ }
424
+ }
425
+
336
426
  @ReactMethod
337
427
  public void startTrip(ReadableMap optionsMap, final Promise promise) {
338
428
  try {
@@ -578,6 +668,7 @@ public class RNRadarModule extends ReactContextBaseJavaModule implements Permiss
578
668
  }
579
669
  int radius = optionsMap.hasKey("radius") ? optionsMap.getInt("radius") : 1000;
580
670
  String[] chains = optionsMap.hasKey("chains") ? RNRadarUtils.stringArrayForArray(optionsMap.getArray("chains")) : null;
671
+ Map<String, String> chainMetadata = RNRadarUtils.stringStringMap(optionsMap.getMap("chainMetadata"));
581
672
  String[] categories = optionsMap.hasKey("categories") ? RNRadarUtils.stringArrayForArray(optionsMap.getArray("categories")) : null;
582
673
  String[] groups = optionsMap.hasKey("groups") ? RNRadarUtils.stringArrayForArray(optionsMap.getArray("groups")) : null;
583
674
  int limit = optionsMap.hasKey("limit") ? optionsMap.getInt("limit") : 10;
@@ -607,9 +698,9 @@ public class RNRadarModule extends ReactContextBaseJavaModule implements Permiss
607
698
  };
608
699
 
609
700
  if (near != null) {
610
- Radar.searchPlaces(near, radius, chains, categories, groups, limit, callback);
701
+ Radar.searchPlaces(near, radius, chains, chainMetadata, categories, groups, limit, callback);
611
702
  } else {
612
- Radar.searchPlaces(radius, chains, categories, groups, limit, callback);
703
+ Radar.searchPlaces(radius, chains, chainMetadata, categories, groups, limit, callback);
613
704
  }
614
705
  }
615
706
 
@@ -984,4 +1075,39 @@ public class RNRadarModule extends ReactContextBaseJavaModule implements Permiss
984
1075
  });
985
1076
  }
986
1077
 
1078
+ @ReactMethod
1079
+ public void sendEvent(String customType, ReadableMap metadata, final Promise promise) throws JSONException {
1080
+ if (promise == null) {
1081
+ return;
1082
+ }
1083
+
1084
+ JSONObject metadataObj = RNRadarUtils.jsonForMap(metadata);
1085
+ Radar.sendEvent(customType, metadataObj, new Radar.RadarSendEventCallback() {
1086
+ @Override
1087
+ public void onComplete(@NonNull Radar.RadarStatus status, @Nullable Location location, @Nullable RadarEvent[] events, @Nullable RadarUser user) {
1088
+ try {
1089
+ if (status == Radar.RadarStatus.SUCCESS) {
1090
+ WritableMap map = Arguments.createMap();
1091
+ map.putString("status", status.toString());
1092
+ if (location != null) {
1093
+ map.putMap("location", RNRadarUtils.mapForJson(Radar.jsonForLocation(location)));
1094
+ }
1095
+ if (events != null) {
1096
+ map.putArray("events", RNRadarUtils.arrayForJson(RadarEvent.toJson(events)));
1097
+ }
1098
+ if (user != null) {
1099
+ map.putMap("user", RNRadarUtils.mapForJson(user.toJson()));
1100
+ }
1101
+ promise.resolve(map);
1102
+ } else {
1103
+ promise.reject(status.toString(), status.toString());
1104
+ }
1105
+ } catch (JSONException e) {
1106
+ Log.e(TAG, "JSONException", e);
1107
+ promise.reject(Radar.RadarStatus.ERROR_SERVER.toString(), Radar.RadarStatus.ERROR_SERVER.toString());
1108
+ }
1109
+ }
1110
+ });
1111
+ }
1112
+
987
1113
  }
@@ -3,6 +3,8 @@ package io.radar.react;
3
3
  import com.facebook.react.bridge.ReadableArray;
4
4
  import com.facebook.react.bridge.ReadableMapKeySetIterator;
5
5
  import java.util.Iterator;
6
+ import java.util.Map;
7
+ import java.util.HashMap;
6
8
 
7
9
  import org.json.JSONArray;
8
10
  import org.json.JSONException;
@@ -148,5 +150,18 @@ class RNRadarUtils {
148
150
  }
149
151
  return arr;
150
152
  }
153
+ static Map<String, String> stringStringMap(ReadableMap readableMap) {
154
+ if (readableMap == null) {
155
+ return null;
156
+ }
157
+
158
+ Map<String, String> stringMap = new HashMap<String, String>();
159
+ ReadableMapKeySetIterator iterator = readableMap.keySetIterator();
160
+ while (iterator.hasNextKey()) {
161
+ String key = iterator.nextKey();
162
+ stringMap.put(key, readableMap.getString(key));
163
+ }
164
+ return stringMap;
165
+ }
151
166
 
152
167
  }
package/ios/RNRadar.m CHANGED
@@ -111,14 +111,34 @@ RCT_EXPORT_METHOD(setUserId:(NSString *)userId) {
111
111
  [Radar setUserId:userId];
112
112
  }
113
113
 
114
+ RCT_EXPORT_METHOD(getUserId:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
115
+ resolve([Radar getUserId]);
116
+ }
117
+
114
118
  RCT_EXPORT_METHOD(setDescription:(NSString *)description) {
115
119
  [Radar setDescription:description];
116
120
  }
117
121
 
122
+ RCT_EXPORT_METHOD(getDescription:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
123
+ resolve([Radar getDescription]);
124
+ }
125
+
118
126
  RCT_EXPORT_METHOD(setMetadata:(NSDictionary *)metadataDict) {
119
127
  [Radar setMetadata:metadataDict];
120
128
  }
121
129
 
130
+ RCT_EXPORT_METHOD(getMetadata:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
131
+ resolve([Radar getMetadata]);
132
+ }
133
+
134
+ RCT_EXPORT_METHOD(setAnonymousTrackingEnabled:(BOOL)enabled) {
135
+ [Radar setAnonymousTrackingEnabled:enabled];
136
+ }
137
+
138
+ RCT_EXPORT_METHOD(setAdIdEnabled:(BOOL)enabled) {
139
+ [Radar setAdIdEnabled:enabled];
140
+ }
141
+
122
142
  RCT_REMAP_METHOD(getPermissionsStatus, getPermissionsStatusWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
123
143
  CLAuthorizationStatus status = [CLLocationManager authorizationStatus];
124
144
  NSString *statusStr;
@@ -158,11 +178,29 @@ RCT_EXPORT_METHOD(requestPermissions:(BOOL)background resolve:(RCTPromiseResolve
158
178
  }
159
179
  }
160
180
 
161
- RCT_EXPORT_METHOD(getLocation:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
181
+ RCT_EXPORT_METHOD(getLocation:(NSString *)desiredAccuracy resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
162
182
  __block RCTPromiseResolveBlock resolver = resolve;
163
183
  __block RCTPromiseRejectBlock rejecter = reject;
184
+ RadarTrackingOptionsDesiredAccuracy accuracy = RadarTrackingOptionsDesiredAccuracyMedium;
185
+
186
+ if (desiredAccuracy) {
187
+ NSString *lowerAccuracy = [desiredAccuracy lowercaseString];
188
+ if ([lowerAccuracy isEqualToString:@"high"]) {
189
+ accuracy = RadarTrackingOptionsDesiredAccuracyHigh;
190
+ } else if ([lowerAccuracy isEqualToString:@"medium"]) {
191
+ accuracy = RadarTrackingOptionsDesiredAccuracyMedium;
192
+ } else if ([lowerAccuracy isEqualToString:@"low"]) {
193
+ accuracy = RadarTrackingOptionsDesiredAccuracyLow;
194
+ } else {
195
+ if (reject) {
196
+ reject([Radar stringForStatus:RadarStatusErrorBadRequest], [Radar stringForStatus:RadarStatusErrorBadRequest], nil);
197
+ }
198
+
199
+ return;
200
+ }
201
+ }
164
202
 
165
- [Radar getLocationWithCompletionHandler:^(RadarStatus status, CLLocation * _Nullable location, BOOL stopped) {
203
+ [Radar getLocationWithDesiredAccuracy:accuracy completionHandler:^(RadarStatus status, CLLocation * _Nullable location, BOOL stopped) {
166
204
  if (status == RadarStatusSuccess && resolver) {
167
205
  NSMutableDictionary *dict = [NSMutableDictionary new];
168
206
  [dict setObject:[Radar stringForStatus:status] forKey:@"status"];
@@ -307,6 +345,20 @@ RCT_EXPORT_METHOD(stopTracking) {
307
345
  [Radar stopTracking];
308
346
  }
309
347
 
348
+ RCT_EXPORT_METHOD(isTracking:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
349
+ BOOL res = [Radar isTracking];
350
+ resolve(@(res));
351
+ }
352
+
353
+ RCT_EXPORT_METHOD(getTrackingOptions:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
354
+ if (resolve == nil) {
355
+ return;
356
+ }
357
+
358
+ RadarTrackingOptions* options = [Radar getTrackingOptions];
359
+ resolve([options dictionaryValue]);
360
+ }
361
+
310
362
  RCT_EXPORT_METHOD(setForegroundServiceOptions) {
311
363
  // not implemented
312
364
  }
@@ -319,8 +371,20 @@ RCT_EXPORT_METHOD(rejectEvent:(NSString *)eventId) {
319
371
  [Radar rejectEventId:eventId];
320
372
  }
321
373
 
374
+ RCT_EXPORT_METHOD(getTripOptions:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
375
+ if (resolve == nil) {
376
+ return;
377
+ }
378
+
379
+ RadarTripOptions* options = [Radar getTripOptions];
380
+ resolve([options dictionaryValue]);
381
+ }
382
+
322
383
  RCT_EXPORT_METHOD(startTrip:(NSDictionary *)optionsDict resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
323
384
  RadarTripOptions *options = [RadarTripOptions tripOptionsFromDictionary:optionsDict];
385
+ if (options.scheduledArrivalAt) {
386
+ options.scheduledArrivalAt = [RCTConvert NSDate:options.scheduledArrivalAt];
387
+ }
324
388
 
325
389
  __block RCTPromiseResolveBlock resolver = resolve;
326
390
  __block RCTPromiseRejectBlock rejecter = reject;
@@ -516,6 +580,7 @@ RCT_EXPORT_METHOD(searchPlaces:(NSDictionary *)optionsDict resolve:(RCTPromiseRe
516
580
  radius = 1000;
517
581
  }
518
582
  NSArray *chains = optionsDict[@"chains"];
583
+ NSDictionary *chainMetadata = optionsDict[@"chainMetadata"];
519
584
  NSArray *categories = optionsDict[@"categories"];
520
585
  NSArray *groups = optionsDict[@"groups"];
521
586
  NSNumber *limitNumber = optionsDict[@"limit"];
@@ -548,9 +613,9 @@ RCT_EXPORT_METHOD(searchPlaces:(NSDictionary *)optionsDict resolve:(RCTPromiseRe
548
613
  };
549
614
 
550
615
  if (near) {
551
- [Radar searchPlacesNear:near radius:radius chains:chains categories:categories groups:groups limit:limit completionHandler:completionHandler];
616
+ [Radar searchPlacesNear:near radius:radius chains:chains chainMetadata:chainMetadata categories:categories groups:groups limit:limit completionHandler:completionHandler];
552
617
  } else {
553
- [Radar searchPlacesWithRadius:radius chains:chains categories:categories groups:groups limit:limit completionHandler:completionHandler];
618
+ [Radar searchPlacesWithRadius:radius chains:chains chainMetadata:chainMetadata categories:categories groups:groups limit:limit completionHandler:completionHandler];
554
619
  }
555
620
  }
556
621
 
@@ -889,4 +954,29 @@ RCT_EXPORT_METHOD(getMatrix:(NSDictionary *)optionsDict resolve:(RCTPromiseResol
889
954
  }];
890
955
  }
891
956
 
957
+ RCT_EXPORT_METHOD(sendEvent:(NSString*) customType metadata:(NSDictionary *)metadata resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
958
+ __block RCTPromiseResolveBlock resolver = resolve;
959
+ __block RCTPromiseRejectBlock rejecter = reject;
960
+
961
+ [Radar sendEvent:customType withMetadata:metadata completionHandler:^(RadarStatus status, CLLocation * _Nullable location, NSArray<RadarEvent *> * _Nullable events, RadarUser * _Nullable user) {
962
+ if (status == RadarStatusSuccess && resolver) {
963
+ NSMutableDictionary *dict = [NSMutableDictionary new];
964
+ [dict setObject:[Radar stringForStatus:status] forKey:@"status"];
965
+ if (location) {
966
+ [dict setObject:[Radar dictionaryForLocation:location] forKey:@"location"];
967
+ }
968
+ if (events) {
969
+ [dict setObject:[RadarEvent arrayForEvents:events] forKey:@"events"];
970
+ }
971
+ if (user) {
972
+ [dict setObject:[user dictionaryValue] forKey:@"user"];
973
+ }
974
+ resolver(dict);
975
+ } else if (rejecter) {
976
+ rejecter([Radar stringForStatus:status], [Radar stringForStatus:status], nil);
977
+ }
978
+ resolver = nil;
979
+ rejecter = nil;
980
+ }];
981
+ }
892
982
  @end
@@ -14,14 +14,34 @@ const setUserId = (userId) => {
14
14
  NativeModules.RNRadar.setUserId(userId);
15
15
  };
16
16
 
17
+ const getUserId = () => (
18
+ NativeModules.RNRadar.getUserId()
19
+ );
20
+
17
21
  const setDescription = (description) => {
18
22
  NativeModules.RNRadar.setDescription(description);
19
23
  };
20
24
 
25
+ const getDescription = () => (
26
+ NativeModules.RNRadar.getDescription()
27
+ );
28
+
21
29
  const setMetadata = (metadata) => {
22
30
  NativeModules.RNRadar.setMetadata(metadata);
23
31
  };
24
32
 
33
+ const getMetadata = () => (
34
+ NativeModules.RNRadar.getMetadata()
35
+ )
36
+
37
+ const setAnonymousTrackingEnabled = (enabled) => (
38
+ NativeModules.RNRadar.setAnonymousTrackingEnabled(enabled)
39
+ )
40
+
41
+ const setAdIdEnabled = (enabled) => (
42
+ NativeModules.RNRadar.setAdIdEnabled(enabled)
43
+ )
44
+
25
45
  const getPermissionsStatus = () => (
26
46
  NativeModules.RNRadar.getPermissionsStatus()
27
47
  );
@@ -30,8 +50,8 @@ const requestPermissions = background => (
30
50
  NativeModules.RNRadar.requestPermissions(background)
31
51
  );
32
52
 
33
- const getLocation = () => (
34
- NativeModules.RNRadar.getLocation()
53
+ const getLocation = desiredAccuracy => (
54
+ NativeModules.RNRadar.getLocation(desiredAccuracy)
35
55
  );
36
56
 
37
57
  const trackOnce = options => {
@@ -70,10 +90,22 @@ const stopTracking = () => (
70
90
  NativeModules.RNRadar.stopTracking()
71
91
  );
72
92
 
93
+ const getTrackingOptions = () => (
94
+ NativeModules.RNRadar.getTrackingOptions()
95
+ )
96
+
97
+ const isTracking = () => (
98
+ NativeModules.RNRadar.isTracking()
99
+ )
100
+
73
101
  const setForegroundServiceOptions = options => (
74
102
  NativeModules.RNRadar.setForegroundServiceOptions(options)
75
103
  );
76
104
 
105
+ const getTripOptions = () => (
106
+ NativeModules.RNRadar.getTripOptions()
107
+ )
108
+
77
109
  const startTrip = options => (
78
110
  NativeModules.RNRadar.startTrip(options)
79
111
  );
@@ -134,6 +166,10 @@ const getMatrix = options => (
134
166
  NativeModules.RNRadar.getMatrix(options)
135
167
  );
136
168
 
169
+ const sendEvent = (customType, metadata) => (
170
+ NativeModules.RNRadar.sendEvent(customType, metadata)
171
+ )
172
+
137
173
  const on = (event, callback) => (
138
174
  eventEmitter.addListener(event, callback)
139
175
  );
@@ -149,8 +185,13 @@ const off = (event, callback) => {
149
185
  const Radar = {
150
186
  setLogLevel,
151
187
  setUserId,
188
+ getUserId,
152
189
  setDescription,
190
+ getDescription,
153
191
  setMetadata,
192
+ getMetadata,
193
+ setAnonymousTrackingEnabled,
194
+ setAdIdEnabled,
154
195
  getPermissionsStatus,
155
196
  requestPermissions,
156
197
  getLocation,
@@ -161,9 +202,12 @@ const Radar = {
161
202
  startTrackingCustom,
162
203
  mockTracking,
163
204
  stopTracking,
205
+ isTracking,
206
+ getTrackingOptions,
164
207
  setForegroundServiceOptions,
165
208
  acceptEvent,
166
209
  rejectEvent,
210
+ getTripOptions,
167
211
  startTrip,
168
212
  updateTrip,
169
213
  completeTrip,
@@ -177,6 +221,7 @@ const Radar = {
177
221
  ipGeocode,
178
222
  getDistance,
179
223
  getMatrix,
224
+ sendEvent,
180
225
  on,
181
226
  off,
182
227
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "React Native module for Radar, the leading geofencing and location tracking platform",
4
4
  "homepage": "https://radar.com",
5
5
  "license": "Apache-2.0",
6
- "version": "3.5.6",
6
+ "version": "3.5.7",
7
7
  "main": "js/index.js",
8
8
  "files": [
9
9
  "android",