@josuelmm/cordova-background-geolocation 3.1.0 → 3.2.0

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +55 -0
  2. package/HISTORY.md +6 -0
  3. package/README.md +62 -9
  4. package/RELEASE.MD +15 -4
  5. package/android/CDVBackgroundGeolocation/src/main/java/com/marianhello/bgloc/cordova/ConfigMapper.java +8 -0
  6. package/android/CDVBackgroundGeolocation/src/main/java/com/tenforwardconsulting/bgloc/cordova/BackgroundGeolocationPlugin.java +57 -1
  7. package/android/common/src/main/java/com/marianhello/bgloc/BackgroundGeolocationFacade.java +26 -0
  8. package/android/common/src/main/java/com/marianhello/bgloc/Config.java +44 -0
  9. package/android/common/src/main/java/com/marianhello/bgloc/PostLocationTask.java +13 -0
  10. package/android/common/src/main/java/com/marianhello/bgloc/data/SessionLocationDAO.java +18 -0
  11. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationContract.java +5 -1
  12. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteConfigurationDAO.java +13 -1
  13. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteOpenHelper.java +13 -1
  14. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionContract.java +74 -0
  15. package/android/common/src/main/java/com/marianhello/bgloc/data/sqlite/SQLiteSessionLocationDAO.java +169 -0
  16. package/android/common/src/main/java/com/marianhello/bgloc/service/LocationServiceImpl.java +132 -4
  17. package/angular/background-geolocation.service.ts +28 -0
  18. package/angular/dist/background-geolocation.service.d.ts +4 -0
  19. package/angular/dist/esm2022/background-geolocation.service.mjs +13 -1
  20. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs +12 -0
  21. package/angular/dist/fesm2022/josuelmm-cordova-background-geolocation.mjs.map +1 -1
  22. package/angular/dist/public-api.d.ts +1 -1
  23. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.h +4 -0
  24. package/ios/CDVBackgroundGeolocation/CDVBackgroundGeolocation.m +41 -1
  25. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.h +4 -0
  26. package/ios/common/BackgroundGeolocation/MAURBackgroundGeolocationFacade.m +21 -0
  27. package/ios/common/BackgroundGeolocation/MAURGeolocationOpenHelper.m +12 -3
  28. package/ios/common/BackgroundGeolocation/MAURPostLocationTask.m +5 -0
  29. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.h +29 -0
  30. package/ios/common/BackgroundGeolocation/MAURSessionLocationContract.m +31 -0
  31. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.h +25 -0
  32. package/ios/common/BackgroundGeolocation/MAURSessionLocationDAO.m +153 -0
  33. package/package.json +7 -3
  34. package/plugin.xml +8 -1
  35. package/www/BackgroundGeolocation.d.ts +60 -0
  36. package/www/BackgroundGeolocation.js +24 -0
  37. package/www/cordova-channel-stub.js +27 -0
  38. package/www/cordova-exec-stub.js +15 -0
@@ -0,0 +1,74 @@
1
+ package com.marianhello.bgloc.data.sqlite;
2
+
3
+ import android.provider.BaseColumns;
4
+
5
+ import static com.marianhello.bgloc.data.sqlite.SQLiteOpenHelper.COMMA_SEP;
6
+ import static com.marianhello.bgloc.data.sqlite.SQLiteOpenHelper.INTEGER_TYPE;
7
+ import static com.marianhello.bgloc.data.sqlite.SQLiteOpenHelper.REAL_TYPE;
8
+ import static com.marianhello.bgloc.data.sqlite.SQLiteOpenHelper.TEXT_TYPE;
9
+
10
+ /**
11
+ * Contract for the "session" location table.
12
+ * Stores all locations for the current recording session (route).
13
+ * Cleared on startSession() and clearSession(); not cleared when sync succeeds.
14
+ */
15
+ public final class SQLiteSessionContract {
16
+
17
+ public SQLiteSessionContract() {}
18
+
19
+ public static abstract class SessionEntry implements BaseColumns {
20
+ public static final String TABLE_NAME = "location_session";
21
+ public static final String COLUMN_NAME_NULLABLE = "NULLHACK";
22
+ public static final String COLUMN_NAME_TIME = "time";
23
+ public static final String COLUMN_NAME_ACCURACY = "accuracy";
24
+ public static final String COLUMN_NAME_VERTICAL_ACCURACY = "vertical_accuracy";
25
+ public static final String COLUMN_NAME_SPEED = "speed";
26
+ public static final String COLUMN_NAME_BEARING = "bearing";
27
+ public static final String COLUMN_NAME_ALTITUDE = "altitude";
28
+ public static final String COLUMN_NAME_LATITUDE = "latitude";
29
+ public static final String COLUMN_NAME_LONGITUDE = "longitude";
30
+ public static final String COLUMN_NAME_RADIUS = "radius";
31
+ public static final String COLUMN_NAME_HAS_ACCURACY = "has_accuracy";
32
+ public static final String COLUMN_NAME_HAS_VERTICAL_ACCURACY = "has_vertical_accuracy";
33
+ public static final String COLUMN_NAME_HAS_SPEED = "has_speed";
34
+ public static final String COLUMN_NAME_HAS_BEARING = "has_bearing";
35
+ public static final String COLUMN_NAME_HAS_ALTITUDE = "has_altitude";
36
+ public static final String COLUMN_NAME_HAS_RADIUS = "has_radius";
37
+ public static final String COLUMN_NAME_PROVIDER = "provider";
38
+ public static final String COLUMN_NAME_LOCATION_PROVIDER = "service_provider";
39
+ public static final String COLUMN_NAME_STATUS = "valid";
40
+ public static final String COLUMN_NAME_BATCH_START_MILLIS = "batch_start";
41
+ public static final String COLUMN_NAME_MOCK_FLAGS = "mock_flags";
42
+
43
+ public static final String SQL_CREATE_SESSION_TABLE =
44
+ "CREATE TABLE " + SessionEntry.TABLE_NAME + " (" +
45
+ SessionEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
46
+ SessionEntry.COLUMN_NAME_TIME + INTEGER_TYPE + COMMA_SEP +
47
+ SessionEntry.COLUMN_NAME_ACCURACY + REAL_TYPE + COMMA_SEP +
48
+ SessionEntry.COLUMN_NAME_VERTICAL_ACCURACY + REAL_TYPE + COMMA_SEP +
49
+ SessionEntry.COLUMN_NAME_SPEED + REAL_TYPE + COMMA_SEP +
50
+ SessionEntry.COLUMN_NAME_BEARING + REAL_TYPE + COMMA_SEP +
51
+ SessionEntry.COLUMN_NAME_ALTITUDE + REAL_TYPE + COMMA_SEP +
52
+ SessionEntry.COLUMN_NAME_LATITUDE + REAL_TYPE + COMMA_SEP +
53
+ SessionEntry.COLUMN_NAME_LONGITUDE + REAL_TYPE + COMMA_SEP +
54
+ SessionEntry.COLUMN_NAME_RADIUS + REAL_TYPE + COMMA_SEP +
55
+ SessionEntry.COLUMN_NAME_HAS_ACCURACY + INTEGER_TYPE + COMMA_SEP +
56
+ SessionEntry.COLUMN_NAME_HAS_VERTICAL_ACCURACY + INTEGER_TYPE + COMMA_SEP +
57
+ SessionEntry.COLUMN_NAME_HAS_SPEED + INTEGER_TYPE + COMMA_SEP +
58
+ SessionEntry.COLUMN_NAME_HAS_BEARING + INTEGER_TYPE + COMMA_SEP +
59
+ SessionEntry.COLUMN_NAME_HAS_ALTITUDE + INTEGER_TYPE + COMMA_SEP +
60
+ SessionEntry.COLUMN_NAME_HAS_RADIUS + INTEGER_TYPE + COMMA_SEP +
61
+ SessionEntry.COLUMN_NAME_PROVIDER + TEXT_TYPE + COMMA_SEP +
62
+ SessionEntry.COLUMN_NAME_LOCATION_PROVIDER + INTEGER_TYPE + COMMA_SEP +
63
+ SessionEntry.COLUMN_NAME_STATUS + INTEGER_TYPE + COMMA_SEP +
64
+ SessionEntry.COLUMN_NAME_BATCH_START_MILLIS + INTEGER_TYPE + COMMA_SEP +
65
+ SessionEntry.COLUMN_NAME_MOCK_FLAGS + INTEGER_TYPE +
66
+ " )";
67
+
68
+ public static final String SQL_DROP_SESSION_TABLE =
69
+ "DROP TABLE IF EXISTS " + SessionEntry.TABLE_NAME;
70
+
71
+ public static final String SQL_CREATE_SESSION_TABLE_TIME_IDX =
72
+ "CREATE INDEX session_time_idx ON " + SessionEntry.TABLE_NAME + " (" + SessionEntry.COLUMN_NAME_TIME + ")";
73
+ }
74
+ }
@@ -0,0 +1,169 @@
1
+ package com.marianhello.bgloc.data.sqlite;
2
+
3
+ import android.content.ContentValues;
4
+ import android.content.Context;
5
+ import android.database.Cursor;
6
+ import android.database.sqlite.SQLiteDatabase;
7
+ import com.marianhello.bgloc.data.BackgroundLocation;
8
+ import com.marianhello.bgloc.data.SessionLocationDAO;
9
+
10
+ import java.util.ArrayList;
11
+ import java.util.Collection;
12
+
13
+ public class SQLiteSessionLocationDAO implements SessionLocationDAO {
14
+
15
+ private static final String PREFS_NAME = "bgloc_session";
16
+ private static final String KEY_SESSION_ACTIVE = "session_active";
17
+
18
+ private final SQLiteDatabase db;
19
+ private final Context context;
20
+
21
+ public SQLiteSessionLocationDAO(Context context) {
22
+ this.context = context.getApplicationContext();
23
+ SQLiteOpenHelper helper = SQLiteOpenHelper.getHelper(this.context);
24
+ this.db = helper.getWritableDatabase();
25
+ }
26
+
27
+ @Override
28
+ public void startSession() {
29
+ db.delete(SQLiteSessionContract.SessionEntry.TABLE_NAME, null, null);
30
+ setSessionActive(true);
31
+ }
32
+
33
+ @Override
34
+ public void clearSession() {
35
+ db.delete(SQLiteSessionContract.SessionEntry.TABLE_NAME, null, null);
36
+ setSessionActive(false);
37
+ }
38
+
39
+ @Override
40
+ public boolean isSessionActive() {
41
+ return context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
42
+ .getBoolean(KEY_SESSION_ACTIVE, false);
43
+ }
44
+
45
+ private void setSessionActive(boolean active) {
46
+ context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
47
+ .edit()
48
+ .putBoolean(KEY_SESSION_ACTIVE, active)
49
+ .apply();
50
+ }
51
+
52
+ @Override
53
+ public void persistSessionLocation(BackgroundLocation location) {
54
+ if (!isSessionActive() || location == null) return;
55
+ ContentValues values = getContentValues(location);
56
+ db.insertOrThrow(SQLiteSessionContract.SessionEntry.TABLE_NAME,
57
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_NULLABLE, values);
58
+ }
59
+
60
+ @Override
61
+ public Collection<BackgroundLocation> getSessionLocations() {
62
+ Collection<BackgroundLocation> locations = new ArrayList<>();
63
+ String orderBy = SQLiteSessionContract.SessionEntry.COLUMN_NAME_TIME + " ASC";
64
+ Cursor cursor = null;
65
+ try {
66
+ cursor = db.query(
67
+ SQLiteSessionContract.SessionEntry.TABLE_NAME,
68
+ queryColumns(),
69
+ null, null, null, null, orderBy);
70
+ while (cursor.moveToNext()) {
71
+ locations.add(hydrate(cursor));
72
+ }
73
+ } finally {
74
+ if (cursor != null) cursor.close();
75
+ }
76
+ return locations;
77
+ }
78
+
79
+ @Override
80
+ public int getSessionLocationsCount() {
81
+ Cursor cursor = null;
82
+ try {
83
+ cursor = db.rawQuery("SELECT COUNT(*) FROM " + SQLiteSessionContract.SessionEntry.TABLE_NAME, null);
84
+ return cursor.moveToFirst() ? cursor.getInt(0) : 0;
85
+ } finally {
86
+ if (cursor != null) cursor.close();
87
+ }
88
+ }
89
+
90
+ private BackgroundLocation hydrate(Cursor c) {
91
+ BackgroundLocation l = new BackgroundLocation(c.getString(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_PROVIDER)));
92
+ l.setTime(c.getLong(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_TIME)));
93
+ if (c.getInt(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_ACCURACY)) == 1) {
94
+ l.setAccuracy(c.getFloat(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_ACCURACY)));
95
+ }
96
+ if (c.getInt(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_VERTICAL_ACCURACY)) == 1) {
97
+ l.setVerticalAccuracy(c.getFloat(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_VERTICAL_ACCURACY)));
98
+ }
99
+ if (c.getInt(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_SPEED)) == 1) {
100
+ l.setSpeed(c.getFloat(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_SPEED)));
101
+ }
102
+ if (c.getInt(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_BEARING)) == 1) {
103
+ l.setBearing(c.getFloat(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_BEARING)));
104
+ }
105
+ if (c.getInt(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_ALTITUDE)) == 1) {
106
+ l.setAltitude(c.getDouble(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_ALTITUDE)));
107
+ }
108
+ if (c.getInt(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_RADIUS)) == 1) {
109
+ l.setRadius(c.getFloat(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_RADIUS)));
110
+ }
111
+ l.setLatitude(c.getDouble(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_LATITUDE)));
112
+ l.setLongitude(c.getDouble(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_LONGITUDE)));
113
+ l.setLocationProvider(c.getInt(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_LOCATION_PROVIDER)));
114
+ l.setLocationId(c.getLong(c.getColumnIndex(SQLiteSessionContract.SessionEntry._ID)));
115
+ l.setMockFlags(c.getInt(c.getColumnIndex(SQLiteSessionContract.SessionEntry.COLUMN_NAME_MOCK_FLAGS)));
116
+ return l;
117
+ }
118
+
119
+ private ContentValues getContentValues(BackgroundLocation l) {
120
+ ContentValues values = new ContentValues();
121
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_PROVIDER, l.getProvider());
122
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_TIME, l.getTime());
123
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_ACCURACY, l.getAccuracy());
124
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_VERTICAL_ACCURACY, l.getVerticalAccuracy());
125
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_SPEED, l.getSpeed());
126
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_BEARING, l.getBearing());
127
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_ALTITUDE, l.getAltitude());
128
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_RADIUS, l.getRadius());
129
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_LATITUDE, l.getLatitude());
130
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_LONGITUDE, l.getLongitude());
131
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_ACCURACY, l.hasAccuracy() ? 1 : 0);
132
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_VERTICAL_ACCURACY, l.hasVerticalAccuracy() ? 1 : 0);
133
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_SPEED, l.hasSpeed() ? 1 : 0);
134
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_BEARING, l.hasBearing() ? 1 : 0);
135
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_ALTITUDE, l.hasAltitude() ? 1 : 0);
136
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_RADIUS, l.hasRadius() ? 1 : 0);
137
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_LOCATION_PROVIDER, l.getLocationProvider());
138
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_STATUS, 0);
139
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_BATCH_START_MILLIS, 0L);
140
+ values.put(SQLiteSessionContract.SessionEntry.COLUMN_NAME_MOCK_FLAGS, l.getMockFlags());
141
+ return values;
142
+ }
143
+
144
+ private String[] queryColumns() {
145
+ return new String[]{
146
+ SQLiteSessionContract.SessionEntry._ID,
147
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_PROVIDER,
148
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_TIME,
149
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_ACCURACY,
150
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_VERTICAL_ACCURACY,
151
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_SPEED,
152
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_BEARING,
153
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_ALTITUDE,
154
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_RADIUS,
155
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_LATITUDE,
156
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_LONGITUDE,
157
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_ACCURACY,
158
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_VERTICAL_ACCURACY,
159
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_SPEED,
160
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_BEARING,
161
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_ALTITUDE,
162
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_HAS_RADIUS,
163
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_LOCATION_PROVIDER,
164
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_STATUS,
165
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_BATCH_START_MILLIS,
166
+ SQLiteSessionContract.SessionEntry.COLUMN_NAME_MOCK_FLAGS
167
+ };
168
+ }
169
+ }
@@ -22,6 +22,7 @@ import android.content.ComponentName;
22
22
  import android.content.pm.PackageManager;
23
23
  import android.content.pm.ServiceInfo;
24
24
  import android.Manifest;
25
+ import android.location.Location;
25
26
  import android.net.ConnectivityManager;
26
27
  import android.net.NetworkInfo;
27
28
  import android.os.Binder;
@@ -48,7 +49,9 @@ import com.marianhello.bgloc.data.BackgroundLocation;
48
49
  import com.marianhello.bgloc.data.ConfigurationDAO;
49
50
  import com.marianhello.bgloc.data.DAOFactory;
50
51
  import com.marianhello.bgloc.data.LocationDAO;
52
+ import com.marianhello.bgloc.data.SessionLocationDAO;
51
53
  import com.marianhello.bgloc.data.LocationTransform;
54
+ import com.marianhello.bgloc.data.sqlite.SQLiteSessionLocationDAO;
52
55
  import com.marianhello.bgloc.headless.AbstractTaskRunner;
53
56
  import com.marianhello.bgloc.headless.ActivityTask;
54
57
  import com.marianhello.bgloc.headless.LocationTask;
@@ -67,6 +70,8 @@ import com.marianhello.logging.UncaughtExceptionLogger;
67
70
  import org.chromium.content.browser.ThreadUtils;
68
71
  import org.json.JSONException;
69
72
 
73
+ import java.util.Locale;
74
+
70
75
  import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.containsCommand;
71
76
  import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.containsMessage;
72
77
  import static com.marianhello.bgloc.service.LocationServiceIntentBuilder.getCommand;
@@ -122,6 +127,7 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
122
127
  private HandlerThread mHandlerThread;
123
128
  private ServiceHandler mServiceHandler;
124
129
  private LocationDAO mLocationDAO;
130
+ private SessionLocationDAO mSessionDAO;
125
131
  private PostLocationTask mPostLocationTask;
126
132
  private String mHeadlessTaskRunnerClass;
127
133
  private TaskRunner mHeadlessTaskRunner;
@@ -156,6 +162,30 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
156
162
  }
157
163
  };
158
164
 
165
+ /** Session start time for notification elapsed time (showTime). */
166
+ private volatile long mSessionStartTime = 0L;
167
+ /** Accumulated distance in meters for notification (showDistance). */
168
+ private volatile double mSessionDistanceMeters = 0.0;
169
+ private volatile double mLastLat = 0.0;
170
+ private volatile double mLastLon = 0.0;
171
+ private volatile boolean mHasLastLocation = false;
172
+ private static final long NOTIFICATION_UPDATE_INTERVAL_MS = 1000L;
173
+ private final Runnable mNotificationUpdateRunnable = new Runnable() {
174
+ @Override
175
+ public void run() {
176
+ if (!sIsRunning || !mIsInForeground || mConfig == null) {
177
+ return;
178
+ }
179
+ boolean showTime = Boolean.TRUE.equals(mConfig.getShowTime());
180
+ boolean showDistance = Boolean.TRUE.equals(mConfig.getShowDistance());
181
+ if (!showTime && !showDistance) {
182
+ return;
183
+ }
184
+ updateForegroundNotification();
185
+ mMainHandler.postDelayed(this, NOTIFICATION_UPDATE_INTERVAL_MS);
186
+ }
187
+ };
188
+
159
189
  private static LocationTransform sLocationTransform;
160
190
  private static LocationProviderFactory sLocationProviderFactory;
161
191
 
@@ -228,8 +258,9 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
228
258
  ContentResolver.setSyncAutomatically(mSyncAccount, authority, true);
229
259
 
230
260
  mLocationDAO = DAOFactory.createLocationDAO(this);
261
+ mSessionDAO = new SQLiteSessionLocationDAO(this);
231
262
 
232
- mPostLocationTask = new PostLocationTask(mLocationDAO,
263
+ mPostLocationTask = new PostLocationTask(mLocationDAO, mSessionDAO,
233
264
  new PostLocationTask.PostLocationTaskListener() {
234
265
  @Override
235
266
  public void onRequestedAbortUpdates() {
@@ -418,6 +449,12 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
418
449
  mMainHandler.postDelayed(mWatchdogRunnable, WATCHDOG_INTERVAL_MS);
419
450
  }
420
451
 
452
+ mSessionStartTime = System.currentTimeMillis();
453
+ mSessionDistanceMeters = 0.0;
454
+ mLastLat = 0.0;
455
+ mLastLon = 0.0;
456
+ mHasLastLocation = false;
457
+
421
458
  ThreadUtils.runOnUiThreadBlocking(new Runnable() {
422
459
  @Override
423
460
  public void run() {
@@ -447,6 +484,7 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
447
484
  }
448
485
 
449
486
  mMainHandler.removeCallbacks(mWatchdogRunnable);
487
+ mMainHandler.removeCallbacks(mNotificationUpdateRunnable);
450
488
 
451
489
  if (mWakeLock != null && mWakeLock.isHeld()) {
452
490
  try {
@@ -461,6 +499,7 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
461
499
  mProvider.onStop();
462
500
  }
463
501
 
502
+ mIsInForeground = false;
464
503
  stopForeground(true);
465
504
  stopSelf();
466
505
 
@@ -530,9 +569,10 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
530
569
  return;
531
570
  }
532
571
  Config config = getConfig();
572
+ String contentText = buildNotificationContentText(config);
533
573
  Notification notification = new NotificationHelper.NotificationFactory(this).getNotification(
534
574
  config.getNotificationTitle(),
535
- config.getNotificationText(),
575
+ contentText,
536
576
  config.getLargeNotificationIcon(),
537
577
  config.getSmallNotificationIcon(),
538
578
  config.getNotificationIconColor());
@@ -548,12 +588,14 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
548
588
  super.startForeground(NOTIFICATION_ID, notification);
549
589
  }
550
590
  mIsInForeground = true;
591
+ scheduleNotificationUpdater();
551
592
  }
552
593
  }
553
594
 
554
595
  @Override
555
596
  public synchronized void stopForeground() {
556
597
  if (sIsRunning && mIsInForeground) {
598
+ mMainHandler.removeCallbacks(mNotificationUpdateRunnable);
557
599
  stopForeground(true);
558
600
  if (mProvider != null) {
559
601
  mProvider.onCommand(LocationProvider.CMD_SWITCH_MODE,
@@ -563,6 +605,70 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
563
605
  }
564
606
  }
565
607
 
608
+ /** Resource names for optional app-localized notification labels (showTime / showDistance). */
609
+ private static final String RES_NOTIFICATION_TIME_LABEL = "plugin_bgloc_notification_time_label";
610
+ private static final String RES_NOTIFICATION_DISTANCE_LABEL = "plugin_bgloc_notification_distance_label";
611
+
612
+ private String getNotificationLabel(String resourceName, String defaultValue) {
613
+ Context app = getApplicationContext();
614
+ int id = app.getResources().getIdentifier(resourceName, "string", app.getPackageName());
615
+ return (id != 0) ? app.getString(id) : defaultValue;
616
+ }
617
+
618
+ private String buildNotificationContentText(Config config) {
619
+ String base = config.getNotificationText() != null ? config.getNotificationText() : "ENABLED";
620
+ if (Boolean.TRUE.equals(config.getShowTime())) {
621
+ String timeLabel = getNotificationLabel(RES_NOTIFICATION_TIME_LABEL, "Time");
622
+ base += "\n" + timeLabel + ": " + formatElapsed(mSessionStartTime);
623
+ }
624
+ if (Boolean.TRUE.equals(config.getShowDistance())) {
625
+ String distanceLabel = getNotificationLabel(RES_NOTIFICATION_DISTANCE_LABEL, "Distance");
626
+ base += "\n" + distanceLabel + ": " + formatDistance(mSessionDistanceMeters);
627
+ }
628
+ return base;
629
+ }
630
+
631
+ private static String formatElapsed(long startTimeMs) {
632
+ long elapsed = Math.max(0L, System.currentTimeMillis() - startTimeMs);
633
+ long s = (elapsed / 1000L) % 60L;
634
+ long m = (elapsed / 60000L) % 60L;
635
+ long h = elapsed / 3600000L;
636
+ return String.format(Locale.US, "%02d:%02d:%02d", h, m, s);
637
+ }
638
+
639
+ private static String formatDistance(double meters) {
640
+ return String.format(Locale.US, "%.2f km", meters / 1000.0);
641
+ }
642
+
643
+ private void updateForegroundNotification() {
644
+ if (!sIsRunning || !mIsInForeground || mConfig == null) {
645
+ return;
646
+ }
647
+ String contentText = buildNotificationContentText(mConfig);
648
+ Notification notification = new NotificationHelper.NotificationFactory(this).getNotification(
649
+ mConfig.getNotificationTitle(),
650
+ contentText,
651
+ mConfig.getLargeNotificationIcon(),
652
+ mConfig.getSmallNotificationIcon(),
653
+ mConfig.getNotificationIconColor());
654
+ NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
655
+ if (nm != null) {
656
+ nm.notify(NOTIFICATION_ID, notification);
657
+ }
658
+ }
659
+
660
+ private void scheduleNotificationUpdater() {
661
+ mMainHandler.removeCallbacks(mNotificationUpdateRunnable);
662
+ if (mConfig == null) {
663
+ return;
664
+ }
665
+ boolean showTime = Boolean.TRUE.equals(mConfig.getShowTime());
666
+ boolean showDistance = Boolean.TRUE.equals(mConfig.getShowDistance());
667
+ if ((showTime || showDistance) && sIsRunning && mIsInForeground) {
668
+ mMainHandler.postDelayed(mNotificationUpdateRunnable, NOTIFICATION_UPDATE_INTERVAL_MS);
669
+ }
670
+ }
671
+
566
672
  @Override
567
673
  public synchronized void configure(Config config) {
568
674
  if (mConfig == null) {
@@ -580,7 +686,7 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
580
686
  public void run() {
581
687
  if (sIsRunning) {
582
688
  if (currentConfig.getStartForeground() == true && mConfig.getStartForeground() == false) {
583
- stopForeground(true);
689
+ stopForeground();
584
690
  }
585
691
 
586
692
  if (mConfig.getStartForeground() == true) {
@@ -589,15 +695,17 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
589
695
  startForeground();
590
696
  } else {
591
697
  // was running in foreground, so just update existing notification
698
+ String contentText = buildNotificationContentText(mConfig);
592
699
  Notification notification = new NotificationHelper.NotificationFactory(LocationServiceImpl.this).getNotification(
593
700
  mConfig.getNotificationTitle(),
594
- mConfig.getNotificationText(),
701
+ contentText,
595
702
  mConfig.getLargeNotificationIcon(),
596
703
  mConfig.getSmallNotificationIcon(),
597
704
  mConfig.getNotificationIconColor());
598
705
 
599
706
  NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
600
707
  notificationManager.notify(NOTIFICATION_ID, notification);
708
+ scheduleNotificationUpdater();
601
709
  }
602
710
  }
603
711
  }
@@ -661,6 +769,26 @@ public class LocationServiceImpl extends Service implements ProviderDelegate, Lo
661
769
  @Override
662
770
  public void onLocation(BackgroundLocation location) {
663
771
  mLastLocationTime = System.currentTimeMillis();
772
+ if (Boolean.TRUE.equals(mConfig != null ? mConfig.getShowDistance() : null)) {
773
+ double lat = location.getLatitude();
774
+ double lon = location.getLongitude();
775
+ if (mHasLastLocation) {
776
+ float[] dist = new float[1];
777
+ Location.distanceBetween(mLastLat, mLastLon, lat, lon, dist);
778
+ mSessionDistanceMeters += (double) dist[0];
779
+ }
780
+ mLastLat = lat;
781
+ mLastLon = lon;
782
+ mHasLastLocation = true;
783
+ if (mIsInForeground && mConfig != null && (Boolean.TRUE.equals(mConfig.getShowTime()) || Boolean.TRUE.equals(mConfig.getShowDistance()))) {
784
+ mMainHandler.post(new Runnable() {
785
+ @Override
786
+ public void run() {
787
+ updateForegroundNotification();
788
+ }
789
+ });
790
+ }
791
+ }
664
792
  logger.debug("New location {}", location.toString());
665
793
 
666
794
  location = transformLocation(location);
@@ -168,6 +168,34 @@ export class BackgroundGeolocationService {
168
168
  return this.ensurePlugin().getPendingSyncCount(success, fail);
169
169
  }
170
170
 
171
+ startSession(
172
+ success?: () => void,
173
+ fail?: (error: any) => void
174
+ ): Promise<void> {
175
+ return this.ensurePlugin().startSession(success, fail);
176
+ }
177
+
178
+ getSessionLocations(
179
+ success?: (locations: any[]) => void,
180
+ fail?: (error: any) => void
181
+ ): Promise<any[]> {
182
+ return this.ensurePlugin().getSessionLocations(success, fail);
183
+ }
184
+
185
+ clearSession(
186
+ success?: () => void,
187
+ fail?: (error: any) => void
188
+ ): Promise<void> {
189
+ return this.ensurePlugin().clearSession(success, fail);
190
+ }
191
+
192
+ getSessionLocationsCount(
193
+ success?: (count: number) => void,
194
+ fail?: (error: any) => void
195
+ ): Promise<number> {
196
+ return this.ensurePlugin().getSessionLocationsCount(success, fail);
197
+ }
198
+
171
199
  getConfig(
172
200
  success?: (config: any) => void,
173
201
  fail?: (error: any) => void
@@ -45,6 +45,10 @@ export declare class BackgroundGeolocationService {
45
45
  forceSync(success?: () => void, fail?: (error: any) => void): Promise<void>;
46
46
  clearSync(success?: () => void, fail?: (error: any) => void): Promise<void>;
47
47
  getPendingSyncCount(success?: (count: number) => void, fail?: (error: any) => void): Promise<number>;
48
+ startSession(success?: () => void, fail?: (error: any) => void): Promise<void>;
49
+ getSessionLocations(success?: (locations: any[]) => void, fail?: (error: any) => void): Promise<any[]>;
50
+ clearSession(success?: () => void, fail?: (error: any) => void): Promise<void>;
51
+ getSessionLocationsCount(success?: (count: number) => void, fail?: (error: any) => void): Promise<number>;
48
52
  getConfig(success?: (config: any) => void, fail?: (error: any) => void): Promise<any>;
49
53
  getLogEntries(limit: number, fromId: number, minLevel: string, success?: (entries: any[]) => void, fail?: (error: any) => void): Promise<any[]>;
50
54
  removeAllListeners(event?: string): void;
@@ -97,6 +97,18 @@ export class BackgroundGeolocationService {
97
97
  getPendingSyncCount(success, fail) {
98
98
  return this.ensurePlugin().getPendingSyncCount(success, fail);
99
99
  }
100
+ startSession(success, fail) {
101
+ return this.ensurePlugin().startSession(success, fail);
102
+ }
103
+ getSessionLocations(success, fail) {
104
+ return this.ensurePlugin().getSessionLocations(success, fail);
105
+ }
106
+ clearSession(success, fail) {
107
+ return this.ensurePlugin().clearSession(success, fail);
108
+ }
109
+ getSessionLocationsCount(success, fail) {
110
+ return this.ensurePlugin().getSessionLocationsCount(success, fail);
111
+ }
100
112
  getConfig(success, fail) {
101
113
  return this.ensurePlugin().getConfig(success, fail);
102
114
  }
@@ -151,4 +163,4 @@ export class BackgroundGeolocationService {
151
163
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BackgroundGeolocationService, decorators: [{
152
164
  type: Injectable
153
165
  }] });
154
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZC1nZW9sb2NhdGlvbi5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vYmFja2dyb3VuZC1nZW9sb2NhdGlvbi5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDOztBQUUzRDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sOEJBQThCLEdBQUcsSUFBSSxjQUFjLENBQzlELDhCQUE4QixDQUMvQixDQUFDO0FBRUY7Ozs7Ozs7Ozs7R0FVRztBQUVILE1BQU0sT0FBTyw0QkFBNEI7SUFFdkMsOERBQThEO0lBQzlELElBQVksTUFBTTtRQUNoQixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVc7WUFBRSxPQUFPLElBQUksQ0FBQztRQUMvQyxPQUFRLE1BQWMsQ0FBQyxxQkFBcUIsSUFBSSxJQUFJLENBQUM7SUFDdkQsQ0FBQztJQUVPLFlBQVk7UUFDbEIsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUN0QixJQUFJLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDUCxNQUFNLElBQUksS0FBSyxDQUNiLHdJQUF3SSxDQUN6SSxDQUFDO1FBQ0osQ0FBQztRQUNELE9BQU8sQ0FBQyxDQUFDO0lBQ1gsQ0FBQztJQUVELFNBQVMsQ0FBQyxPQUFZLEVBQUUsT0FBb0IsRUFBRSxJQUF5QjtRQUNyRSxPQUFPLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMvRCxDQUFDO0lBRUQsS0FBSztRQUNILE9BQU8sSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3JDLENBQUM7SUFFRCxJQUFJO1FBQ0YsT0FBTyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDcEMsQ0FBQztJQUVE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QkFBNEI7a0JBRHhDLFVBQVUiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBJbmplY3RhYmxlLCBJbmplY3Rpb25Ub2tlbiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuXG4vKipcbiAqIFRva2VuIHVzZWQgdG8gcHJvdmlkZSBCYWNrZ3JvdW5kR2VvbG9jYXRpb25TZXJ2aWNlIHdpdGhvdXQgdHJpZ2dlcmluZyBKSVQuXG4gKiBUaGUgbW9kdWxlIHByb3ZpZGVzIHRoaXMgdG9rZW4gd2l0aCB1c2VGYWN0b3J5OyB0aGUgY2xhc3MgaXMgYWxpYXNlZCB2aWEgdXNlRXhpc3RpbmdcbiAqIHNvIHlvdSBzdGlsbCBpbmplY3QgY29uc3RydWN0b3IocHJpdmF0ZSBiZzogQmFja2dyb3VuZEdlb2xvY2F0aW9uU2VydmljZSkge30uXG4gKi9cbmV4cG9ydCBjb25zdCBCQUNLR1JPVU5EX0dFT0xPQ0FUSU9OX1NFUlZJQ0UgPSBuZXcgSW5qZWN0aW9uVG9rZW48QmFja2dyb3VuZEdlb2xvY2F0aW9uU2VydmljZT4oXG4gICdCYWNrZ3JvdW5kR2VvbG9jYXRpb25TZXJ2aWNlJ1xuKTtcblxuLyoqXG4gKiBBbmd1bGFyIHNlcnZpY2UgdGhhdCB3cmFwcyB0aGUgQ29yZG92YS9DYXBhY2l0b3IgQmFja2dyb3VuZEdlb2xvY2F0aW9uIHBsdWdpbi5cbiAqIFVzZSBkZXBlbmRlbmN5IGluamVjdGlvbiBpbnN0ZWFkIG9mIHRoZSBnbG9iYWwgQmFja2dyb3VuZEdlb2xvY2F0aW9uIG9iamVjdC5cbiAqXG4gKiBUaGUgbmF0aXZlIHBsdWdpbiBtdXN0IGJlIGluc3RhbGxlZCBhbmQgYXZhaWxhYmxlIChlLmcuIGFmdGVyIGRldmljZXJlYWR5KS5cbiAqIFR5cGVzIChDb25maWd1cmVPcHRpb25zLCBMb2NhdGlvbiwgZXRjLikgY2FuIGJlIGltcG9ydGVkIGZyb21cbiAqICdAam9zdWVsbW0vY29yZG92YS1iYWNrZ3JvdW5kLWdlb2xvY2F0aW9uJy5cbiAqXG4gKiBQcm92aWRlZCB2aWEgQmFja2dyb3VuZEdlb2xvY2F0aW9uTW9kdWxlIHVzaW5nIGFuIEluamVjdGlvblRva2VuICsgdXNlRmFjdG9yeVxuICogc28gQU9UIGJ1aWxkcyBuZXZlciBuZWVkIHRoZSBKSVQgY29tcGlsZXIgZm9yIHRoaXMgY2xhc3MuXG4gKi9cbkBJbmplY3RhYmxlKClcbmV4cG9ydCBjbGFzcyBCYWNrZ3JvdW5kR2VvbG9jYXRpb25TZXJ2aWNlIHtcblxuICAvKiogUmV0dXJucyB0aGUgZ2xvYmFsIHBsdWdpbiBpbnN0YW5jZSAoQ29yZG92YS9DYXBhY2l0b3IpLiAqL1xuICBwcml2YXRlIGdldCBwbHVnaW4oKTogYW55IHtcbiAgICBpZiAodHlwZW9mIHdpbmRvdyA9PT0gJ3VuZGVmaW5lZCcpIHJldHVybiBudWxsO1xuICAgIHJldHVybiAod2luZG93IGFzIGFueSkuQmFja2dyb3VuZEdlb2xvY2F0aW9uIHx8IG51bGw7XG4gIH1cblxuICBwcml2YXRlIGVuc3VyZVBsdWdpbigpOiBhbnkge1xuICAgIGNvbnN0IHAgPSB0aGlzLnBsdWdpbjtcbiAgICBpZiAoIXApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgJ0JhY2tncm91bmRHZW9sb2NhdGlvbiBpcyBub3QgYXZhaWxhYmxlLiBFbnN1cmUgdGhlIHBsdWdpbiBpcyBpbnN0YWxsZWQgYW5kIHRoZSBhcHAgaXMgcnVubmluZyBpbiBhIG5hdGl2ZSBjb250ZXh0IChDb3Jkb3ZhL0NhcGFjaXRvcikuJ1xuICAgICAgKTtcbiAgICB9XG4gICAgcmV0dXJuIHA7XG4gIH1cblxuICBjb25maWd1cmUob3B0aW9uczogYW55LCBzdWNjZXNzPzogKCkgPT4gdm9pZCwgZmFpbD86IChlcnI6IGFueSkgPT4gdm9pZCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmNvbmZpZ3VyZShvcHRpb25zLCBzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIHN0YXJ0KCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLnN0YXJ0KCk7XG4gIH1cblxuICBzdG9wKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLnN0b3AoKTtcbiAgfVxuXG4gIC8qKiBJbmZvcm0gdGhlIG5hdGl2ZSBwbHVnaW4gdGhhdCB0aGUgYmFja2dyb3VuZCB0YXNrIG1heSBjb21wbGV0ZSAoaU9TKS4gQ2FsbCBhZnRlciBoYW5kbGluZyBsb2NhdGlvbi9zdGF0aW9uYXJ5IGluIHRoZSBjYWxsYmFjay4gKi9cbiAgZmluaXNoKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmZpbmlzaCgpO1xuICB9XG5cbiAgLyoqIEZvcmNlIHRoZSBwbHVnaW4gdG8gZW50ZXIgXCJtb3ZpbmdcIiBvciBcInN0YXRpb25hcnlcIiBzdGF0ZSAoaU9TKS4gKi9cbiAgY2hhbmdlUGFjZShpc01vdmluZzogYm9vbGVhbik6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmNoYW5nZVBhY2UoaXNNb3ZpbmcpO1xuICB9XG5cbiAgZ2V0Q3VycmVudExvY2F0aW9uKFxuICAgIHN1Y2Nlc3M/OiAobG9jYXRpb246IGFueSkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWQsXG4gICAgb3B0aW9ucz86IGFueVxuICApOiBQcm9taXNlPGFueT4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmdldEN1cnJlbnRMb2NhdGlvbihzdWNjZXNzLCBmYWlsLCBvcHRpb25zKTtcbiAgfVxuXG4gIGdldFN0YXRpb25hcnlMb2NhdGlvbihcbiAgICBzdWNjZXNzPzogKGxvY2F0aW9uOiBhbnkpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8YW55PiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZ2V0U3RhdGlvbmFyeUxvY2F0aW9uKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgY2hlY2tTdGF0dXMoXG4gICAgc3VjY2Vzcz86IChzdGF0dXM6IGFueSkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTxhbnk+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5jaGVja1N0YXR1cyhzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIHNob3dBcHBTZXR0aW5ncygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5zaG93QXBwU2V0dGluZ3MoKTtcbiAgfVxuXG4gIC8qKiBPcGVuIGFwcCBzZXR0aW5ncyAoYWxpYXMgZm9yIHNob3dBcHBTZXR0aW5ncykuICovXG4gIG9wZW5TZXR0aW5ncygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5vcGVuU2V0dGluZ3MoKTtcbiAgfVxuXG4gIHNob3dMb2NhdGlvblNldHRpbmdzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLnNob3dMb2NhdGlvblNldHRpbmdzKCk7XG4gIH1cblxuICBnZXRQbHVnaW5WZXJzaW9uKFxuICAgIHN1Y2Nlc3M/OiAodmVyc2lvbjogc3RyaW5nKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmdldFBsdWdpblZlcnNpb24oc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBnZXRMb2NhdGlvbnMoXG4gICAgc3VjY2Vzcz86IChsb2NhdGlvbnM6IGFueVtdKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPGFueVtdPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZ2V0TG9jYXRpb25zKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgZ2V0VmFsaWRMb2NhdGlvbnMoXG4gICAgc3VjY2Vzcz86IChsb2NhdGlvbnM6IGFueVtdKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPGFueVtdPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZ2V0VmFsaWRMb2NhdGlvbnMoc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBnZXRWYWxpZExvY2F0aW9uc0FuZERlbGV0ZShcbiAgICBzdWNjZXNzPzogKGxvY2F0aW9uczogYW55W10pID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8YW55W10+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5nZXRWYWxpZExvY2F0aW9uc0FuZERlbGV0ZShzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGRlbGV0ZUxvY2F0aW9uKFxuICAgIGxvY2F0aW9uSWQ6IG51bWJlcixcbiAgICBzdWNjZXNzPzogKCkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZGVsZXRlTG9jYXRpb24obG9jYXRpb25JZCwgc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBkZWxldGVBbGxMb2NhdGlvbnMoXG4gICAgc3VjY2Vzcz86ICgpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmRlbGV0ZUFsbExvY2F0aW9ucyhzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIHN3aXRjaE1vZGUoXG4gICAgbW9kZUlkOiBudW1iZXIsXG4gICAgc3VjY2Vzcz86ICgpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLnN3aXRjaE1vZGUobW9kZUlkLCBzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGZvcmNlU3luYyhcbiAgICBzdWNjZXNzPzogKCkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZm9yY2VTeW5jKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgY2xlYXJTeW5jKFxuICAgIHN1Y2Nlc3M/OiAoKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5jbGVhclN5bmMoc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBnZXRQZW5kaW5nU3luY0NvdW50KFxuICAgIHN1Y2Nlc3M/OiAoY291bnQ6IG51bWJlcikgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5nZXRQZW5kaW5nU3luY0NvdW50KHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgZ2V0Q29uZmlnKFxuICAgIHN1Y2Nlc3M/OiAoY29uZmlnOiBhbnkpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8YW55PiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZ2V0Q29uZmlnKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgZ2V0TG9nRW50cmllcyhcbiAgICBsaW1pdDogbnVtYmVyLFxuICAgIGZyb21JZDogbnVtYmVyLFxuICAgIG1pbkxldmVsOiBzdHJpbmcsXG4gICAgc3VjY2Vzcz86IChlbnRyaWVzOiBhbnlbXSkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTxhbnlbXT4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmdldExvZ0VudHJpZXMobGltaXQsIGZyb21JZCwgbWluTGV2ZWwsIHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgcmVtb3ZlQWxsTGlzdGVuZXJzKGV2ZW50Pzogc3RyaW5nKTogdm9pZCB7XG4gICAgdGhpcy5lbnN1cmVQbHVnaW4oKS5yZW1vdmVBbGxMaXN0ZW5lcnMoZXZlbnQpO1xuICB9XG5cbiAgc3RhcnRUYXNrKFxuICAgIHN1Y2Nlc3M/OiAodGFza0tleTogbnVtYmVyKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLnN0YXJ0VGFzayhzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGVuZFRhc2soXG4gICAgdGFza0tleTogbnVtYmVyLFxuICAgIHN1Y2Nlc3M/OiAoKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5lbmRUYXNrKHRhc2tLZXksIHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgaGVhZGxlc3NUYXNrKHRhc2s6IChldmVudDogYW55KSA9PiB2b2lkKTogdm9pZCB7XG4gICAgdGhpcy5lbnN1cmVQbHVnaW4oKS5oZWFkbGVzc1Rhc2sodGFzayk7XG4gIH1cblxuICAvKipcbiAgICogUmVnaXN0ZXIgYW4gZXZlbnQgbGlzdGVuZXIuIENvbXBhdGlibGUgd2l0aCBBd2Vzb21lLXN0eWxlIHVzYWdlOlxuICAgKiAub24oQmFja2dyb3VuZEdlb2xvY2F0aW9uRXZlbnRzLmVycm9yKS5zdWJzY3JpYmUoKGVycikgPT4gLi4uKSDigJQgcmV0dXJucyBzdWJzY3JpcHRpb24gd2l0aCAudW5zdWJzY3JpYmUoKS5cbiAgICogLm9uKCdsb2NhdGlvbicsIChsb2MpID0+IC4uLikg4oCUIGFsc28gc3VwcG9ydGVkOyByZXR1cm5lZCBvYmplY3QgaGFzIC51bnN1YnNjcmliZSgpLlxuICAgKi9cbiAgb24oZXZlbnROYW1lOiBzdHJpbmcsIGNhbGxiYWNrPzogKHZhbHVlOiBhbnkpID0+IHZvaWQpOiB7IHN1YnNjcmliZShjYjogKHZhbHVlOiBhbnkpID0+IHZvaWQpOiB7IHVuc3Vic2NyaWJlKCk6IHZvaWQgfTsgdW5zdWJzY3JpYmUoKTogdm9pZCB9IHtcbiAgICBjb25zdCBwbHVnaW4gPSB0aGlzLmVuc3VyZVBsdWdpbigpO1xuICAgIGlmIChjYWxsYmFjayAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBjb25zdCBzdWIgPSBwbHVnaW4ub24oZXZlbnROYW1lLCBjYWxsYmFjaykgYXMgeyByZW1vdmU/OiAoKSA9PiB2b2lkIH07XG4gICAgICByZXR1cm4ge1xuICAgICAgICBzdWJzY3JpYmUoY2I6ICh2YWx1ZTogYW55KSA9PiB2b2lkKSB7XG4gICAgICAgICAgY29uc3QgcyA9IGNiID8gKHBsdWdpbi5vbihldmVudE5hbWUsIGNiKSBhcyB7IHJlbW92ZT86ICgpID0+IHZvaWQgfSkgOiBudWxsO1xuICAgICAgICAgIHJldHVybiB7IHVuc3Vic2NyaWJlKCkgeyBzPy5yZW1vdmU/LigpOyB9IH07XG4gICAgICAgIH0sXG4gICAgICAgIHVuc3Vic2NyaWJlKCkgeyBzdWIucmVtb3ZlPy4oKTsgfVxuICAgICAgfTtcbiAgICB9XG4gICAgY29uc3QgY2hhbm5lbCA9IHBsdWdpbi5vbihldmVudE5hbWUpIGFzIHsgc3Vic2NyaWJlOiAoY2I6ICh2OiBhbnkpID0+IHZvaWQpID0+IHZvaWQ7IHVuc3Vic2NyaWJlOiAoY2I6ICh2OiBhbnkpID0+IHZvaWQpID0+IHZvaWQgfTtcbiAgICByZXR1cm4ge1xuICAgICAgc3Vic2NyaWJlKGNiOiAodmFsdWU6IGFueSkgPT4gdm9pZCkge1xuICAgICAgICBjaGFubmVsLnN1YnNjcmliZShjYik7XG4gICAgICAgIHJldHVybiB7IHVuc3Vic2NyaWJlKCkgeyBjaGFubmVsLnVuc3Vic2NyaWJlKGNiKTsgfSB9O1xuICAgICAgfSxcbiAgICAgIHVuc3Vic2NyaWJlKCkgeyAvKiBuby1vcCB3aGVuIG5vIGNhbGxiYWNrICovIH1cbiAgICB9O1xuICB9XG5cbiAgLyoqIENvbnZlbmllbmNlOiBhY2Nlc3MgcGx1Z2luIGNvbnN0YW50cyAoZS5nLiBBQ1RJVklUWV9QUk9WSURFUiwgSElHSF9BQ0NVUkFDWSkuICovXG4gIGdldCBuYXRpdmUoKTogYW55IHtcbiAgICByZXR1cm4gdGhpcy5wbHVnaW47XG4gIH1cbn1cbiJdfQ==
166
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmFja2dyb3VuZC1nZW9sb2NhdGlvbi5zZXJ2aWNlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vYmFja2dyb3VuZC1nZW9sb2NhdGlvbi5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxVQUFVLEVBQUUsY0FBYyxFQUFFLE1BQU0sZUFBZSxDQUFDOztBQUUzRDs7OztHQUlHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sOEJBQThCLEdBQUcsSUFBSSxjQUFjLENBQzlELDhCQUE4QixDQUMvQixDQUFDO0FBRUY7Ozs7Ozs7Ozs7R0FVRztBQUVILE1BQU0sT0FBTyw0QkFBNEI7SUFFdkMsOERBQThEO0lBQzlELElBQVksTUFBTTtRQUNoQixJQUFJLE9BQU8sTUFBTSxLQUFLLFdBQVc7WUFBRSxPQUFPLElBQUksQ0FBQztRQUMvQyxPQUFRLE1BQWMsQ0FBQyxxQkFBcUIsSUFBSSxJQUFJLENBQUM7SUFDdkQsQ0FBQztJQUVPLFlBQVk7UUFDbEIsTUFBTSxDQUFDLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUN0QixJQUFJLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDUCxNQUFNLElBQUksS0FBSyxDQUNiLHdJQUF3SSxDQUN6SSxDQUFDO1FBQ0osQ0FBQztRQUNELE9BQU8sQ0FBQyxDQUFDO0lBQ1gsQ0FBQztJQUVELFNBQVMsQ0FBQyxPQUFZLEVBQUUsT0FBb0IsRUFBRSxJQUF5QjtRQUNyRSxPQUFPLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQyxTQUFTLENBQUMsT0FBTyxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsQ0FBQztJQUMvRCxDQUFDO0lBRUQsS0FBSztRQUNILE9BQU8sSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDLEtBQUssRUFBRSxDQUFDO0lBQ3JDLENBQUM7SUFFRCxJQUFJO1FBQ0YsT0FBTyxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDcEMsQ0FBQztJQUVE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RUFBRSxDQUFDO2dCQUM5QyxDQUFDO2dCQUNELFdBQVcsS0FBSyxHQUFHLENBQUMsTUFBTSxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUM7YUFDbEMsQ0FBQztRQUNKLENBQUM7UUFDRCxNQUFNLE9BQU8sR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDLFNBQVMsQ0FBK0YsQ0FBQztRQUNuSSxPQUFPO1lBQ0wsU0FBUyxDQUFDLEVBQXdCO2dCQUNoQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUN0QixPQUFPLEVBQUUsV0FBVyxLQUFLLE9BQU8sQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN4RCxDQUFDO1lBQ0QsV0FBVyxLQUFrQyxDQUFDO1NBQy9DLENBQUM7SUFDSixDQUFDO0lBRUQsb0ZBQW9GO0lBQ3BGLElBQUksTUFBTTtRQUNSLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNyQixDQUFDO3dHQXJQVSw0QkFBNEI7NEdBQTVCLDRCQUE0Qjs7NEZBQTVCLDRCQUE0QjtrQkFEeEMsVUFBVSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEluamVjdGFibGUsIEluamVjdGlvblRva2VuIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbi8qKlxuICogVG9rZW4gdXNlZCB0byBwcm92aWRlIEJhY2tncm91bmRHZW9sb2NhdGlvblNlcnZpY2Ugd2l0aG91dCB0cmlnZ2VyaW5nIEpJVC5cbiAqIFRoZSBtb2R1bGUgcHJvdmlkZXMgdGhpcyB0b2tlbiB3aXRoIHVzZUZhY3Rvcnk7IHRoZSBjbGFzcyBpcyBhbGlhc2VkIHZpYSB1c2VFeGlzdGluZ1xuICogc28geW91IHN0aWxsIGluamVjdCBjb25zdHJ1Y3Rvcihwcml2YXRlIGJnOiBCYWNrZ3JvdW5kR2VvbG9jYXRpb25TZXJ2aWNlKSB7fS5cbiAqL1xuZXhwb3J0IGNvbnN0IEJBQ0tHUk9VTkRfR0VPTE9DQVRJT05fU0VSVklDRSA9IG5ldyBJbmplY3Rpb25Ub2tlbjxCYWNrZ3JvdW5kR2VvbG9jYXRpb25TZXJ2aWNlPihcbiAgJ0JhY2tncm91bmRHZW9sb2NhdGlvblNlcnZpY2UnXG4pO1xuXG4vKipcbiAqIEFuZ3VsYXIgc2VydmljZSB0aGF0IHdyYXBzIHRoZSBDb3Jkb3ZhL0NhcGFjaXRvciBCYWNrZ3JvdW5kR2VvbG9jYXRpb24gcGx1Z2luLlxuICogVXNlIGRlcGVuZGVuY3kgaW5qZWN0aW9uIGluc3RlYWQgb2YgdGhlIGdsb2JhbCBCYWNrZ3JvdW5kR2VvbG9jYXRpb24gb2JqZWN0LlxuICpcbiAqIFRoZSBuYXRpdmUgcGx1Z2luIG11c3QgYmUgaW5zdGFsbGVkIGFuZCBhdmFpbGFibGUgKGUuZy4gYWZ0ZXIgZGV2aWNlcmVhZHkpLlxuICogVHlwZXMgKENvbmZpZ3VyZU9wdGlvbnMsIExvY2F0aW9uLCBldGMuKSBjYW4gYmUgaW1wb3J0ZWQgZnJvbVxuICogJ0Bqb3N1ZWxtbS9jb3Jkb3ZhLWJhY2tncm91bmQtZ2VvbG9jYXRpb24nLlxuICpcbiAqIFByb3ZpZGVkIHZpYSBCYWNrZ3JvdW5kR2VvbG9jYXRpb25Nb2R1bGUgdXNpbmcgYW4gSW5qZWN0aW9uVG9rZW4gKyB1c2VGYWN0b3J5XG4gKiBzbyBBT1QgYnVpbGRzIG5ldmVyIG5lZWQgdGhlIEpJVCBjb21waWxlciBmb3IgdGhpcyBjbGFzcy5cbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIEJhY2tncm91bmRHZW9sb2NhdGlvblNlcnZpY2Uge1xuXG4gIC8qKiBSZXR1cm5zIHRoZSBnbG9iYWwgcGx1Z2luIGluc3RhbmNlIChDb3Jkb3ZhL0NhcGFjaXRvcikuICovXG4gIHByaXZhdGUgZ2V0IHBsdWdpbigpOiBhbnkge1xuICAgIGlmICh0eXBlb2Ygd2luZG93ID09PSAndW5kZWZpbmVkJykgcmV0dXJuIG51bGw7XG4gICAgcmV0dXJuICh3aW5kb3cgYXMgYW55KS5CYWNrZ3JvdW5kR2VvbG9jYXRpb24gfHwgbnVsbDtcbiAgfVxuXG4gIHByaXZhdGUgZW5zdXJlUGx1Z2luKCk6IGFueSB7XG4gICAgY29uc3QgcCA9IHRoaXMucGx1Z2luO1xuICAgIGlmICghcCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnQmFja2dyb3VuZEdlb2xvY2F0aW9uIGlzIG5vdCBhdmFpbGFibGUuIEVuc3VyZSB0aGUgcGx1Z2luIGlzIGluc3RhbGxlZCBhbmQgdGhlIGFwcCBpcyBydW5uaW5nIGluIGEgbmF0aXZlIGNvbnRleHQgKENvcmRvdmEvQ2FwYWNpdG9yKS4nXG4gICAgICApO1xuICAgIH1cbiAgICByZXR1cm4gcDtcbiAgfVxuXG4gIGNvbmZpZ3VyZShvcHRpb25zOiBhbnksIHN1Y2Nlc3M/OiAoKSA9PiB2b2lkLCBmYWlsPzogKGVycjogYW55KSA9PiB2b2lkKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuY29uZmlndXJlKG9wdGlvbnMsIHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgc3RhcnQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuc3RhcnQoKTtcbiAgfVxuXG4gIHN0b3AoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuc3RvcCgpO1xuICB9XG5cbiAgLyoqIEluZm9ybSB0aGUgbmF0aXZlIHBsdWdpbiB0aGF0IHRoZSBiYWNrZ3JvdW5kIHRhc2sgbWF5IGNvbXBsZXRlIChpT1MpLiBDYWxsIGFmdGVyIGhhbmRsaW5nIGxvY2F0aW9uL3N0YXRpb25hcnkgaW4gdGhlIGNhbGxiYWNrLiAqL1xuICBmaW5pc2goKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZmluaXNoKCk7XG4gIH1cblxuICAvKiogRm9yY2UgdGhlIHBsdWdpbiB0byBlbnRlciBcIm1vdmluZ1wiIG9yIFwic3RhdGlvbmFyeVwiIHN0YXRlIChpT1MpLiAqL1xuICBjaGFuZ2VQYWNlKGlzTW92aW5nOiBib29sZWFuKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuY2hhbmdlUGFjZShpc01vdmluZyk7XG4gIH1cblxuICBnZXRDdXJyZW50TG9jYXRpb24oXG4gICAgc3VjY2Vzcz86IChsb2NhdGlvbjogYW55KSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZCxcbiAgICBvcHRpb25zPzogYW55XG4gICk6IFByb21pc2U8YW55PiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZ2V0Q3VycmVudExvY2F0aW9uKHN1Y2Nlc3MsIGZhaWwsIG9wdGlvbnMpO1xuICB9XG5cbiAgZ2V0U3RhdGlvbmFyeUxvY2F0aW9uKFxuICAgIHN1Y2Nlc3M/OiAobG9jYXRpb246IGFueSkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTxhbnk+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5nZXRTdGF0aW9uYXJ5TG9jYXRpb24oc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBjaGVja1N0YXR1cyhcbiAgICBzdWNjZXNzPzogKHN0YXR1czogYW55KSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPGFueT4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmNoZWNrU3RhdHVzKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgc2hvd0FwcFNldHRpbmdzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLnNob3dBcHBTZXR0aW5ncygpO1xuICB9XG5cbiAgLyoqIE9wZW4gYXBwIHNldHRpbmdzIChhbGlhcyBmb3Igc2hvd0FwcFNldHRpbmdzKS4gKi9cbiAgb3BlblNldHRpbmdzKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLm9wZW5TZXR0aW5ncygpO1xuICB9XG5cbiAgc2hvd0xvY2F0aW9uU2V0dGluZ3MoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuc2hvd0xvY2F0aW9uU2V0dGluZ3MoKTtcbiAgfVxuXG4gIGdldFBsdWdpblZlcnNpb24oXG4gICAgc3VjY2Vzcz86ICh2ZXJzaW9uOiBzdHJpbmcpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZ2V0UGx1Z2luVmVyc2lvbihzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGdldExvY2F0aW9ucyhcbiAgICBzdWNjZXNzPzogKGxvY2F0aW9uczogYW55W10pID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8YW55W10+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5nZXRMb2NhdGlvbnMoc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBnZXRWYWxpZExvY2F0aW9ucyhcbiAgICBzdWNjZXNzPzogKGxvY2F0aW9uczogYW55W10pID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8YW55W10+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5nZXRWYWxpZExvY2F0aW9ucyhzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGdldFZhbGlkTG9jYXRpb25zQW5kRGVsZXRlKFxuICAgIHN1Y2Nlc3M/OiAobG9jYXRpb25zOiBhbnlbXSkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTxhbnlbXT4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmdldFZhbGlkTG9jYXRpb25zQW5kRGVsZXRlKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgZGVsZXRlTG9jYXRpb24oXG4gICAgbG9jYXRpb25JZDogbnVtYmVyLFxuICAgIHN1Y2Nlc3M/OiAoKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5kZWxldGVMb2NhdGlvbihsb2NhdGlvbklkLCBzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGRlbGV0ZUFsbExvY2F0aW9ucyhcbiAgICBzdWNjZXNzPzogKCkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZGVsZXRlQWxsTG9jYXRpb25zKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgc3dpdGNoTW9kZShcbiAgICBtb2RlSWQ6IG51bWJlcixcbiAgICBzdWNjZXNzPzogKCkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuc3dpdGNoTW9kZShtb2RlSWQsIHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgZm9yY2VTeW5jKFxuICAgIHN1Y2Nlc3M/OiAoKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPHZvaWQ+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5mb3JjZVN5bmMoc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBjbGVhclN5bmMoXG4gICAgc3VjY2Vzcz86ICgpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmNsZWFyU3luYyhzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGdldFBlbmRpbmdTeW5jQ291bnQoXG4gICAgc3VjY2Vzcz86IChjb3VudDogbnVtYmVyKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPG51bWJlcj4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmdldFBlbmRpbmdTeW5jQ291bnQoc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBzdGFydFNlc3Npb24oXG4gICAgc3VjY2Vzcz86ICgpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLnN0YXJ0U2Vzc2lvbihzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGdldFNlc3Npb25Mb2NhdGlvbnMoXG4gICAgc3VjY2Vzcz86IChsb2NhdGlvbnM6IGFueVtdKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPGFueVtdPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZ2V0U2Vzc2lvbkxvY2F0aW9ucyhzdWNjZXNzLCBmYWlsKTtcbiAgfVxuXG4gIGNsZWFyU2Vzc2lvbihcbiAgICBzdWNjZXNzPzogKCkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuY2xlYXJTZXNzaW9uKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgZ2V0U2Vzc2lvbkxvY2F0aW9uc0NvdW50KFxuICAgIHN1Y2Nlc3M/OiAoY291bnQ6IG51bWJlcikgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTxudW1iZXI+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5nZXRTZXNzaW9uTG9jYXRpb25zQ291bnQoc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBnZXRDb25maWcoXG4gICAgc3VjY2Vzcz86IChjb25maWc6IGFueSkgPT4gdm9pZCxcbiAgICBmYWlsPzogKGVycm9yOiBhbnkpID0+IHZvaWRcbiAgKTogUHJvbWlzZTxhbnk+IHtcbiAgICByZXR1cm4gdGhpcy5lbnN1cmVQbHVnaW4oKS5nZXRDb25maWcoc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBnZXRMb2dFbnRyaWVzKFxuICAgIGxpbWl0OiBudW1iZXIsXG4gICAgZnJvbUlkOiBudW1iZXIsXG4gICAgbWluTGV2ZWw6IHN0cmluZyxcbiAgICBzdWNjZXNzPzogKGVudHJpZXM6IGFueVtdKSA9PiB2b2lkLFxuICAgIGZhaWw/OiAoZXJyb3I6IGFueSkgPT4gdm9pZFxuICApOiBQcm9taXNlPGFueVtdPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuZ2V0TG9nRW50cmllcyhsaW1pdCwgZnJvbUlkLCBtaW5MZXZlbCwgc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICByZW1vdmVBbGxMaXN0ZW5lcnMoZXZlbnQ/OiBzdHJpbmcpOiB2b2lkIHtcbiAgICB0aGlzLmVuc3VyZVBsdWdpbigpLnJlbW92ZUFsbExpc3RlbmVycyhldmVudCk7XG4gIH1cblxuICBzdGFydFRhc2soXG4gICAgc3VjY2Vzcz86ICh0YXNrS2V5OiBudW1iZXIpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8bnVtYmVyPiB7XG4gICAgcmV0dXJuIHRoaXMuZW5zdXJlUGx1Z2luKCkuc3RhcnRUYXNrKHN1Y2Nlc3MsIGZhaWwpO1xuICB9XG5cbiAgZW5kVGFzayhcbiAgICB0YXNrS2V5OiBudW1iZXIsXG4gICAgc3VjY2Vzcz86ICgpID0+IHZvaWQsXG4gICAgZmFpbD86IChlcnJvcjogYW55KSA9PiB2b2lkXG4gICk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0aGlzLmVuc3VyZVBsdWdpbigpLmVuZFRhc2sodGFza0tleSwgc3VjY2VzcywgZmFpbCk7XG4gIH1cblxuICBoZWFkbGVzc1Rhc2sodGFzazogKGV2ZW50OiBhbnkpID0+IHZvaWQpOiB2b2lkIHtcbiAgICB0aGlzLmVuc3VyZVBsdWdpbigpLmhlYWRsZXNzVGFzayh0YXNrKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWdpc3RlciBhbiBldmVudCBsaXN0ZW5lci4gQ29tcGF0aWJsZSB3aXRoIEF3ZXNvbWUtc3R5bGUgdXNhZ2U6XG4gICAqIC5vbihCYWNrZ3JvdW5kR2VvbG9jYXRpb25FdmVudHMuZXJyb3IpLnN1YnNjcmliZSgoZXJyKSA9PiAuLi4pIOKAlCByZXR1cm5zIHN1YnNjcmlwdGlvbiB3aXRoIC51bnN1YnNjcmliZSgpLlxuICAgKiAub24oJ2xvY2F0aW9uJywgKGxvYykgPT4gLi4uKSDigJQgYWxzbyBzdXBwb3J0ZWQ7IHJldHVybmVkIG9iamVjdCBoYXMgLnVuc3Vic2NyaWJlKCkuXG4gICAqL1xuICBvbihldmVudE5hbWU6IHN0cmluZywgY2FsbGJhY2s/OiAodmFsdWU6IGFueSkgPT4gdm9pZCk6IHsgc3Vic2NyaWJlKGNiOiAodmFsdWU6IGFueSkgPT4gdm9pZCk6IHsgdW5zdWJzY3JpYmUoKTogdm9pZCB9OyB1bnN1YnNjcmliZSgpOiB2b2lkIH0ge1xuICAgIGNvbnN0IHBsdWdpbiA9IHRoaXMuZW5zdXJlUGx1Z2luKCk7XG4gICAgaWYgKGNhbGxiYWNrICE9PSB1bmRlZmluZWQpIHtcbiAgICAgIGNvbnN0IHN1YiA9IHBsdWdpbi5vbihldmVudE5hbWUsIGNhbGxiYWNrKSBhcyB7IHJlbW92ZT86ICgpID0+IHZvaWQgfTtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHN1YnNjcmliZShjYjogKHZhbHVlOiBhbnkpID0+IHZvaWQpIHtcbiAgICAgICAgICBjb25zdCBzID0gY2IgPyAocGx1Z2luLm9uKGV2ZW50TmFtZSwgY2IpIGFzIHsgcmVtb3ZlPzogKCkgPT4gdm9pZCB9KSA6IG51bGw7XG4gICAgICAgICAgcmV0dXJuIHsgdW5zdWJzY3JpYmUoKSB7IHM/LnJlbW92ZT8uKCk7IH0gfTtcbiAgICAgICAgfSxcbiAgICAgICAgdW5zdWJzY3JpYmUoKSB7IHN1Yi5yZW1vdmU/LigpOyB9XG4gICAgICB9O1xuICAgIH1cbiAgICBjb25zdCBjaGFubmVsID0gcGx1Z2luLm9uKGV2ZW50TmFtZSkgYXMgeyBzdWJzY3JpYmU6IChjYjogKHY6IGFueSkgPT4gdm9pZCkgPT4gdm9pZDsgdW5zdWJzY3JpYmU6IChjYjogKHY6IGFueSkgPT4gdm9pZCkgPT4gdm9pZCB9O1xuICAgIHJldHVybiB7XG4gICAgICBzdWJzY3JpYmUoY2I6ICh2YWx1ZTogYW55KSA9PiB2b2lkKSB7XG4gICAgICAgIGNoYW5uZWwuc3Vic2NyaWJlKGNiKTtcbiAgICAgICAgcmV0dXJuIHsgdW5zdWJzY3JpYmUoKSB7IGNoYW5uZWwudW5zdWJzY3JpYmUoY2IpOyB9IH07XG4gICAgICB9LFxuICAgICAgdW5zdWJzY3JpYmUoKSB7IC8qIG5vLW9wIHdoZW4gbm8gY2FsbGJhY2sgKi8gfVxuICAgIH07XG4gIH1cblxuICAvKiogQ29udmVuaWVuY2U6IGFjY2VzcyBwbHVnaW4gY29uc3RhbnRzIChlLmcuIEFDVElWSVRZX1BST1ZJREVSLCBISUdIX0FDQ1VSQUNZKS4gKi9cbiAgZ2V0IG5hdGl2ZSgpOiBhbnkge1xuICAgIHJldHVybiB0aGlzLnBsdWdpbjtcbiAgfVxufVxuIl19