Panagiotis Triantafyllou

db fixes

...@@ -47,5 +47,9 @@ ...@@ -47,5 +47,9 @@
47 <receiver 47 <receiver
48 android:name=".receivers.LocationChangedReceiver" 48 android:name=".receivers.LocationChangedReceiver"
49 android:exported="false" /> 49 android:exported="false" />
50 +
51 + <provider
52 + android:name=".utils.WarplyProvider"
53 + android:authorities="ly.warp.sdk.utils.WarplyProvider" />
50 </application> 54 </application>
51 </manifest> 55 </manifest>
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -26,6 +26,11 @@ import java.io.FileNotFoundException; ...@@ -26,6 +26,11 @@ import java.io.FileNotFoundException;
26 import java.io.IOException; 26 import java.io.IOException;
27 import java.util.ArrayList; 27 import java.util.ArrayList;
28 import java.util.List; 28 import java.util.List;
29 +import java.util.concurrent.ExecutorService;
30 +import java.util.concurrent.Executors;
31 +import java.util.concurrent.Future;
32 +import java.util.concurrent.TimeUnit;
33 +import java.util.concurrent.TimeoutException;
29 34
30 import ly.warp.sdk.utils.constants.WarpConstants; 35 import ly.warp.sdk.utils.constants.WarpConstants;
31 36
...@@ -40,9 +45,13 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -40,9 +45,13 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
40 } 45 }
41 46
42 private static final String DB_NAME = "warply.db"; 47 private static final String DB_NAME = "warply.db";
43 - private static final int DB_VERSION = 12; 48 + private static final int DB_VERSION = 13;
44 private static final String KEY_CIPHER = "tn#mpOl3v3Dy1pr@W"; 49 private static final String KEY_CIPHER = "tn#mpOl3v3Dy1pr@W";
45 50
51 + // Timeout constants
52 + private static final int CONNECTION_TIMEOUT_MS = 5000; // 5 seconds for database connection
53 + private static final int OPERATION_TIMEOUT_MS = 5000; // 5 seconds for database operations
54 +
46 //------------------------------ Fields -----------------------------// 55 //------------------------------ Fields -----------------------------//
47 private static String TABLE_REQUESTS = "requests"; 56 private static String TABLE_REQUESTS = "requests";
48 private static String TABLE_PUSH_REQUESTS = "push_requests"; 57 private static String TABLE_PUSH_REQUESTS = "push_requests";
...@@ -119,6 +128,9 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -119,6 +128,9 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
119 private SQLiteDatabase mDb; 128 private SQLiteDatabase mDb;
120 private static WarplyDBHelper mDBHelperInstance; 129 private static WarplyDBHelper mDBHelperInstance;
121 130
131 + // Single-threaded executor for database operations
132 + private final ExecutorService dbExecutor = Executors.newSingleThreadExecutor();
133 +
122 // =========================================================== 134 // ===========================================================
123 // Constructors 135 // Constructors
124 // =========================================================== 136 // ===========================================================
...@@ -145,21 +157,65 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -145,21 +157,65 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
145 } 157 }
146 158
147 /** 159 /**
148 - * If database connection already initialized, return the db. Else create a 160 + * Gets a writable database connection on a background thread with timeout.
149 - * new one 161 + * This prevents ANR issues by ensuring database operations don't block the main thread.
150 */ 162 */
151 private SQLiteDatabase getDb() { 163 private SQLiteDatabase getDb() {
152 - if (mDb == null) 164 + if (mDb == null || !mDb.isOpen()) {
153 - mDb = getWritableDatabase(KEY_CIPHER); 165 + try {
166 + // Submit task to executor and get future
167 + Future<SQLiteDatabase> future = dbExecutor.submit(() -> getWritableDatabase(KEY_CIPHER));
168 +
169 + // Wait for result with timeout
170 + mDb = future.get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
171 +
172 + // Set busy timeout on the database connection
173 + if (mDb != null && mDb.isOpen()) {
174 + mDb.rawExecSQL("PRAGMA busy_timeout = " + CONNECTION_TIMEOUT_MS);
175 + }
176 + } catch (TimeoutException e) {
177 + Log.e("WarplyDBHelper", "Timeout getting writable database connection", e);
178 + // Return null or handle timeout as appropriate
179 + } catch (Exception e) {
180 + Log.e("WarplyDBHelper", "Error getting writable database connection", e);
181 + // Handle other exceptions
182 + }
183 + }
154 return mDb; 184 return mDb;
155 } 185 }
156 186
187 + /**
188 + * Gets a readable database connection on a background thread with timeout.
189 + * This prevents ANR issues by ensuring database operations don't block the main thread.
190 + */
157 private SQLiteDatabase getReadableDb() { 191 private SQLiteDatabase getReadableDb() {
158 - if (mDb == null) 192 + if (mDb == null || !mDb.isOpen()) {
159 - mDb = getReadableDatabase(KEY_CIPHER); 193 + try {
194 + // Submit task to executor and get future
195 + Future<SQLiteDatabase> future = dbExecutor.submit(() -> getReadableDatabase(KEY_CIPHER));
196 +
197 + // Wait for result with timeout
198 + mDb = future.get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
199 +
200 + // Set busy timeout on the database connection
201 + if (mDb != null && mDb.isOpen()) {
202 + mDb.rawExecSQL("PRAGMA busy_timeout = " + CONNECTION_TIMEOUT_MS);
203 + }
204 + } catch (TimeoutException e) {
205 + Log.e("WarplyDBHelper", "Timeout getting readable database connection", e);
206 + // Return null or handle timeout as appropriate
207 + } catch (Exception e) {
208 + Log.e("WarplyDBHelper", "Error getting readable database connection", e);
209 + // Handle other exceptions
210 + }
211 + }
160 return mDb; 212 return mDb;
161 } 213 }
162 214
215 + /**
216 + * Close database connection - should only be called when app is being destroyed
217 + * or when database won't be used for a long time
218 + */
163 private void closeDb() { 219 private void closeDb() {
164 if (mDb != null && mDb.isOpen()) { 220 if (mDb != null && mDb.isOpen()) {
165 mDb.close(); 221 mDb.close();
...@@ -167,8 +223,53 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -167,8 +223,53 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
167 } 223 }
168 } 224 }
169 225
226 + /**
227 + * Initialize database connection on a background thread.
228 + * Call this during app startup to prevent ANR issues.
229 + */
230 + public void initialize() {
231 + if (mDb == null || !mDb.isOpen()) {
232 + try {
233 + // Submit task to executor and get future
234 + Future<SQLiteDatabase> future = dbExecutor.submit(() -> getWritableDatabase(KEY_CIPHER));
235 +
236 + // Wait for result with timeout
237 + mDb = future.get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
238 +
239 + // Set busy timeout on the database connection
240 + if (mDb != null && mDb.isOpen()) {
241 + mDb.rawExecSQL("PRAGMA busy_timeout = " + CONNECTION_TIMEOUT_MS);
242 + }
243 + } catch (TimeoutException e) {
244 + Log.e("WarplyDBHelper", "Timeout initializing database connection", e);
245 + } catch (Exception e) {
246 + Log.e("WarplyDBHelper", "Error initializing database connection", e);
247 + }
248 + }
249 + }
250 +
251 + /**
252 + * Shutdown database connection and executor service.
253 + * Call this when app is being destroyed.
254 + */
255 + public void shutdown() {
256 + closeDb();
257 +
258 + // Shutdown executor service
259 + dbExecutor.shutdown();
260 + try {
261 + if (!dbExecutor.awaitTermination(5, TimeUnit.SECONDS)) {
262 + dbExecutor.shutdownNow();
263 + }
264 + } catch (InterruptedException e) {
265 + dbExecutor.shutdownNow();
266 + Thread.currentThread().interrupt();
267 + }
268 + }
269 +
170 @Override 270 @Override
171 public void onCreate(SQLiteDatabase db) { 271 public void onCreate(SQLiteDatabase db) {
272 + // Create tables
172 db.execSQL(CREATE_TABLE_REQUESTS); 273 db.execSQL(CREATE_TABLE_REQUESTS);
173 db.execSQL(CREATE_TABLE_TAGS); 274 db.execSQL(CREATE_TABLE_TAGS);
174 db.execSQL(CREATE_TABLE_PUSH_REQUESTS); 275 db.execSQL(CREATE_TABLE_PUSH_REQUESTS);
...@@ -176,6 +277,14 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -176,6 +277,14 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
176 db.execSQL(CREATE_TABLE_CLIENT); 277 db.execSQL(CREATE_TABLE_CLIENT);
177 db.execSQL(CREATE_TABLE_AUTH); 278 db.execSQL(CREATE_TABLE_AUTH);
178 db.execSQL(CREATE_TABLE_TELEMATICS); 279 db.execSQL(CREATE_TABLE_TELEMATICS);
280 +
281 + // Create indexes for frequently queried columns to improve performance
282 + db.execSQL("CREATE INDEX IF NOT EXISTS idx_requests_force ON " + TABLE_REQUESTS + "(" + KEY_REQUESTS_FORCE + ")");
283 + db.execSQL("CREATE INDEX IF NOT EXISTS idx_requests_date ON " + TABLE_REQUESTS + "(" + KEY_REQUESTS_DATE_ADDED + ")");
284 + db.execSQL("CREATE INDEX IF NOT EXISTS idx_push_requests_force ON " + TABLE_PUSH_REQUESTS + "(" + KEY_REQUESTS_FORCE + ")");
285 + db.execSQL("CREATE INDEX IF NOT EXISTS idx_push_requests_date ON " + TABLE_PUSH_REQUESTS + "(" + KEY_REQUESTS_DATE_ADDED + ")");
286 + db.execSQL("CREATE INDEX IF NOT EXISTS idx_push_ack_requests_force ON " + TABLE_PUSH_ACK_REQUESTS + "(" + KEY_REQUESTS_FORCE + ")");
287 + db.execSQL("CREATE INDEX IF NOT EXISTS idx_push_ack_requests_date ON " + TABLE_PUSH_ACK_REQUESTS + "(" + KEY_REQUESTS_DATE_ADDED + ")");
179 } 288 }
180 289
181 @Override 290 @Override
...@@ -228,36 +337,36 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -228,36 +337,36 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
228 public synchronized void clearTable(String tableName) { 337 public synchronized void clearTable(String tableName) {
229 SQLiteDatabase db = getDb(); 338 SQLiteDatabase db = getDb();
230 db.delete(tableName, null, null); 339 db.delete(tableName, null, null);
231 - 340 + // Don't close the database here to improve performance
232 - closeDb();
233 } 341 }
234 342
235 public synchronized void insert(String tableName, ContentValues values) { 343 public synchronized void insert(String tableName, ContentValues values) {
236 SQLiteDatabase db = getDb(); 344 SQLiteDatabase db = getDb();
237 db.insert(tableName, null, values); 345 db.insert(tableName, null, values);
238 - 346 + // Don't close the database here to improve performance
239 - closeDb();
240 } 347 }
241 348
242 public synchronized void update(String tableName, ContentValues values) { 349 public synchronized void update(String tableName, ContentValues values) {
243 SQLiteDatabase db = getDb(); 350 SQLiteDatabase db = getDb();
244 db.update(tableName, values, null, null); 351 db.update(tableName, values, null, null);
245 - 352 + // Don't close the database here to improve performance
246 - closeDb();
247 } 353 }
248 354
249 public synchronized boolean isTableNotEmpty(String tableName) { 355 public synchronized boolean isTableNotEmpty(String tableName) {
250 - Cursor cursor = getReadableDb().rawQuery("SELECT COUNT(*) FROM " + tableName, 356 + boolean isNotEmpty = false;
251 - null); 357 + Cursor cursor = null;
252 - if (cursor != null && cursor.moveToFirst()) { 358 + try {
253 - boolean isNotEmpty = cursor.getInt(0) > 0; 359 + cursor = getReadableDb().rawQuery("SELECT COUNT(*) FROM " + tableName, null);
254 - cursor.close(); 360 + if (cursor != null && cursor.moveToFirst()) {
255 - closeDb(); 361 + isNotEmpty = cursor.getInt(0) > 0;
256 - return isNotEmpty; 362 + }
257 - } else { 363 + } finally {
258 - closeDb(); 364 + if (cursor != null) {
259 - return false; 365 + cursor.close();
366 + }
367 + // Don't close the database here to improve performance
260 } 368 }
369 + return isNotEmpty;
261 } 370 }
262 371
263 //------------------------------ Auth -----------------------------// 372 //------------------------------ Auth -----------------------------//
...@@ -288,30 +397,36 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -288,30 +397,36 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
288 @Nullable 397 @Nullable
289 public synchronized String getAuthValue(String columnName) { 398 public synchronized String getAuthValue(String columnName) {
290 String columnValue = ""; 399 String columnValue = "";
291 - Cursor cursor = getReadableDb().query(TABLE_AUTH, new String[]{columnName}, null, null, null, null, null); 400 + Cursor cursor = null;
292 - if (cursor != null && cursor.moveToFirst()) { 401 + try {
293 - cursor.moveToFirst(); 402 + cursor = getReadableDb().query(TABLE_AUTH, new String[]{columnName}, null, null, null, null, null);
294 - columnValue = cursor.getString(cursor.getColumnIndex(columnName)); 403 + if (cursor != null && cursor.moveToFirst()) {
295 - cursor.close(); 404 + columnValue = cursor.getString(cursor.getColumnIndex(columnName));
405 + }
406 + } finally {
407 + if (cursor != null) {
408 + cursor.close();
409 + }
410 + // Don't close the database here to improve performance
296 } 411 }
297 -
298 - closeDb();
299 -
300 return columnValue; 412 return columnValue;
301 } 413 }
302 414
303 @Nullable 415 @Nullable
304 public synchronized String getClientValue(String columnName) { 416 public synchronized String getClientValue(String columnName) {
305 String columnValue = ""; 417 String columnValue = "";
306 - Cursor cursor = getReadableDb().query(TABLE_CLIENT, new String[]{columnName}, null, null, null, null, null); 418 + Cursor cursor = null;
307 - if (cursor != null && cursor.moveToFirst()) { 419 + try {
308 - cursor.moveToFirst(); 420 + cursor = getReadableDb().query(TABLE_CLIENT, new String[]{columnName}, null, null, null, null, null);
309 - columnValue = cursor.getString(cursor.getColumnIndex(columnName)); 421 + if (cursor != null && cursor.moveToFirst()) {
310 - cursor.close(); 422 + columnValue = cursor.getString(cursor.getColumnIndex(columnName));
423 + }
424 + } finally {
425 + if (cursor != null) {
426 + cursor.close();
427 + }
428 + // Don't close the database here to improve performance
311 } 429 }
312 -
313 - closeDb();
314 -
315 return columnValue; 430 return columnValue;
316 } 431 }
317 432
...@@ -324,21 +439,36 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -324,21 +439,36 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
324 } 439 }
325 440
326 //------------------------------ Api requests -----------------------------// 441 //------------------------------ Api requests -----------------------------//
442 + /**
443 + * Gets all requests from the database.
444 + * NOTE: The caller is responsible for closing the returned Cursor when done with it.
445 + */
327 public synchronized Cursor getAllRequests() { 446 public synchronized Cursor getAllRequests() {
447 + // We don't close the database here because the cursor needs it to remain open
328 return getReadableDb().query(TABLE_REQUESTS, 448 return getReadableDb().query(TABLE_REQUESTS,
329 new String[]{KEY_REQUESTS_ID, 449 new String[]{KEY_REQUESTS_ID,
330 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null, 450 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null,
331 KEY_REQUESTS_DATE_ADDED + " asc", "20"); 451 KEY_REQUESTS_DATE_ADDED + " asc", "20");
332 } 452 }
333 453
454 + /**
455 + * Gets all push requests from the database.
456 + * NOTE: The caller is responsible for closing the returned Cursor when done with it.
457 + */
334 public synchronized Cursor getAllPushRequests() { 458 public synchronized Cursor getAllPushRequests() {
459 + // We don't close the database here because the cursor needs it to remain open
335 return getReadableDb().query(TABLE_PUSH_REQUESTS, 460 return getReadableDb().query(TABLE_PUSH_REQUESTS,
336 new String[]{KEY_REQUESTS_ID, 461 new String[]{KEY_REQUESTS_ID,
337 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null, 462 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null,
338 KEY_REQUESTS_DATE_ADDED + " asc", "20"); 463 KEY_REQUESTS_DATE_ADDED + " asc", "20");
339 } 464 }
340 465
466 + /**
467 + * Gets all push acknowledgment requests from the database.
468 + * NOTE: The caller is responsible for closing the returned Cursor when done with it.
469 + */
341 public synchronized Cursor getAllPushAckRequests() { 470 public synchronized Cursor getAllPushAckRequests() {
471 + // We don't close the database here because the cursor needs it to remain open
342 return getReadableDb().query(TABLE_PUSH_ACK_REQUESTS, 472 return getReadableDb().query(TABLE_PUSH_ACK_REQUESTS,
343 new String[]{KEY_REQUESTS_ID, 473 new String[]{KEY_REQUESTS_ID,
344 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null, 474 KEY_REQUESTS_MICROAPP, KEY_REQUESTS_ENTITY}, null, null, null, null,
...@@ -388,163 +518,225 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -388,163 +518,225 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
388 } 518 }
389 519
390 public synchronized long getRequestsInQueueCount() { 520 public synchronized long getRequestsInQueueCount() {
391 - return DatabaseUtils.queryNumEntries(getReadableDb(), TABLE_REQUESTS); 521 + SQLiteDatabase db = getReadableDb();
522 + long count = DatabaseUtils.queryNumEntries(db, TABLE_REQUESTS);
523 + // Don't close the database here to improve performance
524 + return count;
392 } 525 }
393 526
394 public synchronized long getPushRequestsInQueueCount() { 527 public synchronized long getPushRequestsInQueueCount() {
395 - return DatabaseUtils.queryNumEntries(getReadableDb(), TABLE_PUSH_REQUESTS); 528 + SQLiteDatabase db = getReadableDb();
529 + long count = DatabaseUtils.queryNumEntries(db, TABLE_PUSH_REQUESTS);
530 + // Don't close the database here to improve performance
531 + return count;
396 } 532 }
397 533
398 public synchronized long getPushAckRequestsInQueueCount() { 534 public synchronized long getPushAckRequestsInQueueCount() {
399 - return DatabaseUtils.queryNumEntries(getReadableDb(), TABLE_PUSH_ACK_REQUESTS); 535 + SQLiteDatabase db = getReadableDb();
536 + long count = DatabaseUtils.queryNumEntries(db, TABLE_PUSH_ACK_REQUESTS);
537 + // Don't close the database here to improve performance
538 + return count;
400 } 539 }
401 540
402 public synchronized void deleteRequests(Long... ids) { 541 public synchronized void deleteRequests(Long... ids) {
403 - StringBuilder strFilter = new StringBuilder(); 542 + if (ids.length == 0) return;
404 - for (int i = 0; i < ids.length; i++) { 543 +
405 - if (i > 0) { 544 + SQLiteDatabase db = getDb();
406 - strFilter.append(" OR "); 545 + try {
546 + db.beginTransaction();
547 + StringBuilder strFilter = new StringBuilder();
548 + for (int i = 0; i < ids.length; i++) {
549 + if (i > 0) {
550 + strFilter.append(" OR ");
551 + }
552 + strFilter.append(KEY_REQUESTS_ID);
553 + strFilter.append("=");
554 + strFilter.append(ids[i]);
407 } 555 }
408 - strFilter.append(KEY_REQUESTS_ID); 556 + db.delete(TABLE_REQUESTS, strFilter.toString(), null);
409 - strFilter.append("="); 557 + db.setTransactionSuccessful();
410 - strFilter.append(ids[i]); 558 + } finally {
559 + db.endTransaction();
560 + // Don't close the database here to improve performance
411 } 561 }
412 - getDb().delete(TABLE_REQUESTS, strFilter.toString(), null);
413 -
414 - closeDb();
415 } 562 }
416 563
417 public synchronized void deletePushRequests(Long... ids) { 564 public synchronized void deletePushRequests(Long... ids) {
418 - StringBuilder strFilter = new StringBuilder(); 565 + if (ids.length == 0) return;
419 - for (int i = 0; i < ids.length; i++) { 566 +
420 - if (i > 0) { 567 + SQLiteDatabase db = getDb();
421 - strFilter.append(" OR "); 568 + try {
569 + db.beginTransaction();
570 + StringBuilder strFilter = new StringBuilder();
571 + for (int i = 0; i < ids.length; i++) {
572 + if (i > 0) {
573 + strFilter.append(" OR ");
574 + }
575 + strFilter.append(KEY_REQUESTS_ID);
576 + strFilter.append("=");
577 + strFilter.append(ids[i]);
422 } 578 }
423 - strFilter.append(KEY_REQUESTS_ID); 579 + db.delete(TABLE_PUSH_REQUESTS, strFilter.toString(), null);
424 - strFilter.append("="); 580 + db.setTransactionSuccessful();
425 - strFilter.append(ids[i]); 581 + } finally {
582 + db.endTransaction();
583 + // Don't close the database here to improve performance
426 } 584 }
427 - getDb().delete(TABLE_PUSH_REQUESTS, strFilter.toString(), null);
428 -
429 - closeDb();
430 } 585 }
431 586
432 public synchronized void deletePushAckRequests(Long... ids) { 587 public synchronized void deletePushAckRequests(Long... ids) {
433 - StringBuilder strFilter = new StringBuilder(); 588 + if (ids.length == 0) return;
434 - for (int i = 0; i < ids.length; i++) { 589 +
435 - if (i > 0) { 590 + SQLiteDatabase db = getDb();
436 - strFilter.append(" OR "); 591 + try {
592 + db.beginTransaction();
593 + StringBuilder strFilter = new StringBuilder();
594 + for (int i = 0; i < ids.length; i++) {
595 + if (i > 0) {
596 + strFilter.append(" OR ");
597 + }
598 + strFilter.append(KEY_REQUESTS_ID);
599 + strFilter.append("=");
600 + strFilter.append(ids[i]);
437 } 601 }
438 - strFilter.append(KEY_REQUESTS_ID); 602 + db.delete(TABLE_PUSH_ACK_REQUESTS, strFilter.toString(), null);
439 - strFilter.append("="); 603 + db.setTransactionSuccessful();
440 - strFilter.append(ids[i]); 604 + } finally {
605 + db.endTransaction();
606 + // Don't close the database here to improve performance
441 } 607 }
442 - getDb().delete(TABLE_PUSH_ACK_REQUESTS, strFilter.toString(), null);
443 -
444 - closeDb();
445 } 608 }
446 609
447 public synchronized boolean isForceRequestsExist() { 610 public synchronized boolean isForceRequestsExist() {
448 - Cursor cursor = getReadableDb().query(TABLE_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
449 - null, null, null, null);
450 boolean result = false; 611 boolean result = false;
451 - if (cursor != null) { 612 + Cursor cursor = null;
452 - result = cursor.getCount() > 0; 613 + try {
453 - cursor.close(); 614 + cursor = getReadableDb().query(TABLE_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
615 + null, null, null, null);
616 + if (cursor != null) {
617 + result = cursor.getCount() > 0;
618 + }
619 + } finally {
620 + if (cursor != null) {
621 + cursor.close();
622 + }
623 + // Don't close the database here to improve performance
454 } 624 }
455 -
456 - closeDb();
457 -
458 return result; 625 return result;
459 } 626 }
460 627
461 public synchronized boolean isForcePushRequestsExist() { 628 public synchronized boolean isForcePushRequestsExist() {
462 - Cursor cursor = getReadableDb().query(TABLE_PUSH_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
463 - null, null, null, null);
464 boolean result = false; 629 boolean result = false;
465 - if (cursor != null) { 630 + Cursor cursor = null;
466 - result = cursor.getCount() > 0; 631 + try {
467 - cursor.close(); 632 + cursor = getReadableDb().query(TABLE_PUSH_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
633 + null, null, null, null);
634 + if (cursor != null) {
635 + result = cursor.getCount() > 0;
636 + }
637 + } finally {
638 + if (cursor != null) {
639 + cursor.close();
640 + }
641 + // Don't close the database here to improve performance
468 } 642 }
469 -
470 - closeDb();
471 -
472 return result; 643 return result;
473 } 644 }
474 645
475 public synchronized boolean isForcePushAckRequestsExist() { 646 public synchronized boolean isForcePushAckRequestsExist() {
476 - Cursor cursor = getReadableDb().query(TABLE_PUSH_ACK_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
477 - null, null, null, null);
478 boolean result = false; 647 boolean result = false;
479 - if (cursor != null) { 648 + Cursor cursor = null;
480 - result = cursor.getCount() > 0; 649 + try {
481 - cursor.close(); 650 + cursor = getReadableDb().query(TABLE_PUSH_ACK_REQUESTS, null, KEY_REQUESTS_FORCE + "=1",
651 + null, null, null, null);
652 + if (cursor != null) {
653 + result = cursor.getCount() > 0;
654 + }
655 + } finally {
656 + if (cursor != null) {
657 + cursor.close();
658 + }
659 + // Don't close the database here to improve performance
482 } 660 }
483 -
484 - closeDb();
485 -
486 return result; 661 return result;
487 } 662 }
488 663
489 //------------------------------ Tags -----------------------------// 664 //------------------------------ Tags -----------------------------//
490 public synchronized void saveTags(String[] tags) { 665 public synchronized void saveTags(String[] tags) {
491 if (tags != null && tags.length > 0) { 666 if (tags != null && tags.length > 0) {
667 + SQLiteDatabase db = getDb();
492 try { 668 try {
493 - getDb().beginTransaction(); 669 + db.beginTransaction();
494 ContentValues values = new ContentValues(); 670 ContentValues values = new ContentValues();
495 for (String tag : tags) { 671 for (String tag : tags) {
672 + values.clear();
496 values.put(KEY_TAG, tag); 673 values.put(KEY_TAG, tag);
497 values.put(KEY_TAG_LAST_ADD_DATE, System.currentTimeMillis()); 674 values.put(KEY_TAG_LAST_ADD_DATE, System.currentTimeMillis());
498 - insert(TABLE_TAGS, values); 675 + db.insert(TABLE_TAGS, null, values);
499 } 676 }
500 - getDb().setTransactionSuccessful(); 677 + db.setTransactionSuccessful();
501 -
502 } catch (SQLException e) { 678 } catch (SQLException e) {
503 - closeDb();
504 if (WarpConstants.DEBUG) { 679 if (WarpConstants.DEBUG) {
505 e.printStackTrace(); 680 e.printStackTrace();
506 } 681 }
507 } finally { 682 } finally {
508 - getDb().endTransaction(); 683 + db.endTransaction();
509 - 684 + // Don't close the database here to improve performance
510 - closeDb();
511 } 685 }
512 } 686 }
513 } 687 }
514 688
515 public synchronized void saveTelematics(JSONArray jsonArray) { 689 public synchronized void saveTelematics(JSONArray jsonArray) {
516 if (jsonArray != null && jsonArray.length() > 0) { 690 if (jsonArray != null && jsonArray.length() > 0) {
517 - ContentValues values = new ContentValues(); 691 + SQLiteDatabase db = getDb();
518 - for (int i = 0; i < jsonArray.length(); i++) { 692 + try {
519 - JSONObject jsonobject = jsonArray.optJSONObject(i); 693 + db.beginTransaction();
520 - if (jsonobject != null) { 694 + ContentValues values = new ContentValues();
521 - String timestamp = jsonobject.keys().next(); 695 + for (int i = 0; i < jsonArray.length(); i++) {
522 - values.put(KEY_TIMESTAMP, timestamp); 696 + JSONObject jsonobject = jsonArray.optJSONObject(i);
523 - JSONObject jobjData = jsonobject.optJSONObject(timestamp); 697 + if (jsonobject != null) {
524 - if (jobjData != null) { 698 + values.clear();
525 - values.put(KEY_ACCELERATION, jobjData.optDouble(KEY_ACCELERATION)); 699 + String timestamp = jsonobject.keys().next();
526 - values.put(KEY_SPEED, jobjData.optDouble(KEY_SPEED)); 700 + values.put(KEY_TIMESTAMP, timestamp);
701 + JSONObject jobjData = jsonobject.optJSONObject(timestamp);
702 + if (jobjData != null) {
703 + values.put(KEY_ACCELERATION, jobjData.optDouble(KEY_ACCELERATION));
704 + values.put(KEY_SPEED, jobjData.optDouble(KEY_SPEED));
705 + }
706 + db.insert(TABLE_TELEMATICS, null, values);
527 } 707 }
528 - insert(TABLE_TELEMATICS, values);
529 } 708 }
709 + db.setTransactionSuccessful();
710 + } finally {
711 + db.endTransaction();
712 + // Don't close the database here to improve performance
530 } 713 }
531 } 714 }
532 } 715 }
533 716
534 public synchronized void removeTags(String[] tags) { 717 public synchronized void removeTags(String[] tags) {
535 - StringBuilder strFilter = new StringBuilder(); 718 + if (tags.length == 0) return;
536 - for (int i = 0; i < tags.length; i++) { 719 +
537 - if (i > 0) { 720 + SQLiteDatabase db = getDb();
538 - strFilter.append(" OR "); 721 + try {
722 + db.beginTransaction();
723 + StringBuilder strFilter = new StringBuilder();
724 + for (int i = 0; i < tags.length; i++) {
725 + if (i > 0) {
726 + strFilter.append(" OR ");
727 + }
728 + strFilter.append(KEY_TAG);
729 + strFilter.append("=");
730 + strFilter.append("'");
731 + strFilter.append(tags[i]);
732 + strFilter.append("'");
539 } 733 }
540 - strFilter.append(KEY_TAG); 734 + db.delete(TABLE_TAGS, strFilter.toString(), null);
541 - strFilter.append("="); 735 + db.setTransactionSuccessful();
542 - strFilter.append("'"); 736 + } finally {
543 - strFilter.append(tags[i]); 737 + db.endTransaction();
544 - strFilter.append("'"); 738 + // Don't close the database here to improve performance
545 } 739 }
546 - getDb().delete(TABLE_TAGS, strFilter.toString(), null);
547 - closeDb();
548 } 740 }
549 741
550 public synchronized void removeAllTags() { 742 public synchronized void removeAllTags() {
...@@ -553,21 +745,22 @@ public class WarplyDBHelper extends SQLiteOpenHelper { ...@@ -553,21 +745,22 @@ public class WarplyDBHelper extends SQLiteOpenHelper {
553 745
554 @Nullable 746 @Nullable
555 public synchronized String[] getTags() { 747 public synchronized String[] getTags() {
556 -
557 List<String> tags = null; 748 List<String> tags = null;
558 - Cursor cursor = getReadableDb().query(TABLE_TAGS, null, null, null, null, null, null); 749 + Cursor cursor = null;
559 - if (cursor != null) { 750 + try {
560 - 751 + cursor = getReadableDb().query(TABLE_TAGS, null, null, null, null, null, null);
561 - tags = new ArrayList<>(cursor.getCount()); 752 + if (cursor != null) {
562 - while (cursor.moveToNext()) { 753 + tags = new ArrayList<>(cursor.getCount());
563 - tags.add(cursor.getString(cursor 754 + while (cursor.moveToNext()) {
564 - .getColumnIndex(KEY_TAG))); 755 + tags.add(cursor.getString(cursor.getColumnIndex(KEY_TAG)));
756 + }
757 + }
758 + } finally {
759 + if (cursor != null) {
760 + cursor.close();
565 } 761 }
566 - cursor.close(); 762 + // Don't close the database here to improve performance
567 } 763 }
568 -
569 - closeDb();
570 -
571 return tags != null ? tags.toArray(new String[tags.size()]) : null; 764 return tags != null ? tags.toArray(new String[tags.size()]) : null;
572 } 765 }
573 766
......
1 +package ly.warp.sdk.utils;
2 +
3 +import android.content.ContentProvider;
4 +import android.content.ContentValues;
5 +import android.content.Context;
6 +import android.database.Cursor;
7 +import android.net.Uri;
8 +
9 +import androidx.annotation.NonNull;
10 +import androidx.annotation.Nullable;
11 +import androidx.appcompat.app.AppCompatDelegate;
12 +
13 +import ly.warp.sdk.db.WarplyDBHelper;
14 +
15 +/**
16 + * Created by Panagiotis Triantafyllou on 05/Αυγ/2022.
17 + */
18 +public class WarplyProvider extends ContentProvider {
19 +
20 + @Override
21 + public boolean onCreate() {
22 +// AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
23 +
24 + // Initialize the database helper
25 + Context context = getContext();
26 + if (context != null) {
27 + WarplyDBHelper dbHelper = WarplyDBHelper.getInstance(context);
28 + dbHelper.initialize(); // This will initialize the database connection on a background thread
29 + }
30 +// ViewPump.init(ViewPump.builder()
31 +// .addInterceptor(new CalligraphyInterceptor(
32 +// new CalligraphyConfig.Builder()
33 +// .setDefaultFontPath("fonts/pf_square_sans_pro_regular.ttf")
34 +// .setFontAttrId(R.attr.fontPath)
35 +//// .setFontMapper(new FontMapper() {
36 +//// @Override
37 +//// public String map(String font) {
38 +//// return font;
39 +//// }
40 +//// })
41 +// .build()))
42 +// .build());
43 +
44 + return true;
45 + }
46 +
47 + @Override
48 + public void shutdown() {
49 + // Get the database helper instance and shut it down
50 + Context context = getContext();
51 + if (context != null) {
52 + WarplyDBHelper dbHelper = WarplyDBHelper.getInstance(context);
53 + dbHelper.shutdown();
54 + }
55 +
56 + super.shutdown();
57 + }
58 +
59 + @Nullable
60 + @Override
61 + public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
62 + return null;
63 + }
64 +
65 + @Nullable
66 + @Override
67 + public String getType(@NonNull Uri uri) {
68 + return null;
69 + }
70 +
71 + @Nullable
72 + @Override
73 + public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
74 + return null;
75 + }
76 +
77 + @Override
78 + public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
79 + return 0;
80 + }
81 +
82 + @Override
83 + public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
84 + return 0;
85 + }
86 +}