use of com.parse.boltsinternal.Capture in project Parse-SDK-Android by ParsePlatform.
the class OfflineStore method fetchLocallyAsync.
/**
* Gets the data for the given object from the offline database. Returns a task that will be
* completed if data for the object was available. If the object is not in the cache, the task
* will be faulted, with a CACHE_MISS error.
*
* @param object The object to fetch.
* @param db A database connection to use.
*/
/* package for OfflineQueryLogic */
<T extends ParseObject> Task<T> fetchLocallyAsync(final T object, final ParseSQLiteDatabase db) {
final TaskCompletionSource<T> tcs = new TaskCompletionSource<>();
Task<String> uuidTask;
synchronized (lock) {
if (fetchedObjects.containsKey(object)) {
// noinspection unchecked
return (Task<T>) fetchedObjects.get(object);
}
/*
* Put a placeholder so that anyone else who attempts to fetch this object will just wait for
* this call to finish doing it.
*/
// noinspection unchecked
fetchedObjects.put(object, (Task<ParseObject>) tcs.getTask());
uuidTask = objectToUuidMap.get(object);
}
String className = object.getClassName();
String objectId = object.getObjectId();
/*
* If this gets set, then it will contain data from the offline store that needs to be merged
* into the existing object in memory.
*/
Task<String> jsonStringTask = Task.forResult(null);
if (objectId == null) {
// This Object has never been saved to Parse.
if (uuidTask == null) {
/*
* This object was not pulled from the data store or previously saved to it, so there's
* nothing that can be fetched from it. This isn't an error, because it's really convenient
* to try to fetch objects from the offline store just to make sure they are up-to-date, and
* we shouldn't force developers to specially handle this case.
*/
} else {
/*
* This object is a new ParseObject that is known to the data store, but hasn't been
* fetched. The only way this could happen is if the object had previously been stored in
* the offline store, then the object was removed from memory (maybe by rebooting), and then
* a object with a pointer to it was fetched, so we only created the pointer. We need to
* pull the data out of the database using the UUID.
*/
final String[] select = { OfflineSQLiteOpenHelper.KEY_JSON };
final String where = OfflineSQLiteOpenHelper.KEY_UUID + " = ?";
final Capture<String> uuid = new Capture<>();
jsonStringTask = uuidTask.onSuccessTask(task -> {
uuid.set(task.getResult());
String[] args = { uuid.get() };
return db.queryAsync(OfflineSQLiteOpenHelper.TABLE_OBJECTS, select, where, args);
}).onSuccess(task -> {
Cursor cursor = task.getResult();
cursor.moveToFirst();
if (cursor.isAfterLast()) {
cursor.close();
throw new IllegalStateException("Attempted to find non-existent uuid " + uuid.get());
}
String json = cursor.getString(0);
cursor.close();
return json;
});
}
} else {
if (uuidTask != null) {
/*
* This object is an existing ParseObject, and we must've already pulled its data out of the
* offline store, or else we wouldn't know its UUID. This should never happen.
*/
tcs.setError(new IllegalStateException("This object must have already been " + "fetched from the local datastore, but isn't marked as fetched."));
synchronized (lock) {
// Forget we even tried to fetch this object, so that retries will actually...
// retry.
fetchedObjects.remove(object);
}
return tcs.getTask();
}
/*
* We've got a pointer to an existing ParseObject, but we've never pulled its data out of the
* offline store. Since fetching from the server forces a fetch from the offline store, that
* means this is a pointer. We need to try to find any existing entry for this object in the
* database.
*/
String[] select = { OfflineSQLiteOpenHelper.KEY_JSON, OfflineSQLiteOpenHelper.KEY_UUID };
String where = String.format("%s = ? AND %s = ?", OfflineSQLiteOpenHelper.KEY_CLASS_NAME, OfflineSQLiteOpenHelper.KEY_OBJECT_ID);
String[] args = { className, objectId };
jsonStringTask = db.queryAsync(OfflineSQLiteOpenHelper.TABLE_OBJECTS, select, where, args).onSuccess(task -> {
Cursor cursor = task.getResult();
cursor.moveToFirst();
if (cursor.isAfterLast()) {
/*
* This is a pointer that came from Parse that references an object that has
* never been saved in the offline store before. This just means there's no data
* in the store that needs to be merged into the object.
*/
cursor.close();
throw new ParseException(ParseException.CACHE_MISS, "This object is not available in the offline cache.");
}
// we should fetch its data and record its UUID for future
// reference.
String jsonString = cursor.getString(0);
String newUUID = cursor.getString(1);
cursor.close();
synchronized (lock) {
/*
* It's okay to put this object into the uuid map. No one will try to fetch
* it, because it's already in the fetchedObjects map. And no one will try to
* save to it without fetching it first, so everything should be just fine.
*/
objectToUuidMap.put(object, Task.forResult(newUUID));
uuidToObjectMap.put(newUUID, object);
}
return jsonString;
});
}
return jsonStringTask.onSuccessTask((Continuation<String, Task<Void>>) task -> {
String jsonString = task.getResult();
if (jsonString == null) {
return Task.forError(new ParseException(ParseException.CACHE_MISS, "Attempted to fetch an object offline which was never saved to the offline cache."));
}
final JSONObject json;
try {
json = new JSONObject(jsonString);
} catch (JSONException e) {
return Task.forError(e);
}
final Map<String, Task<ParseObject>> offlineObjects = new HashMap<>();
(new ParseTraverser() {
@Override
protected boolean visit(Object object1) {
if (object1 instanceof JSONObject && ((JSONObject) object1).optString("__type").equals("OfflineObject")) {
String uuid = ((JSONObject) object1).optString("uuid");
offlineObjects.put(uuid, getPointerAsync(uuid, db));
}
return true;
}
}).setTraverseParseObjects(false).setYieldRoot(false).traverse(json);
return Task.whenAll(offlineObjects.values()).onSuccess(task1 -> {
object.mergeREST(object.getState(), json, new OfflineDecoder(offlineObjects));
return null;
});
}).continueWithTask(task -> {
if (task.isCancelled()) {
tcs.setCancelled();
} else if (task.isFaulted()) {
tcs.setError(task.getError());
} else {
tcs.setResult(object);
}
return tcs.getTask();
});
}
use of com.parse.boltsinternal.Capture in project Parse-SDK-Android by ParsePlatform.
the class LocationNotifier method getCurrentLocationAsync.
/**
* Asynchronously gets the current location of the device.
*
* <p>This will request location updates from the best provider that match the given criteria
* and return the first location received.
*
* <p>You can customize the criteria to meet your specific needs. * For higher accuracy, you can
* set {@link Criteria#setAccuracy(int)}, however result in longer times for a fix. * For better
* battery efficiency and faster location fixes, you can set {@link
* Criteria#setPowerRequirement(int)}, however, this will result in lower accuracy.
*
* @param context The context used to request location updates.
* @param timeout The number of milliseconds to allow before timing out.
* @param criteria The application criteria for selecting a location provider.
* @see android.location.LocationManager#getBestProvider(android.location.Criteria, boolean)
* @see android.location.LocationManager#requestLocationUpdates(String, long, float,
* android.location.LocationListener)
*/
/* package */
static Task<Location> getCurrentLocationAsync(Context context, long timeout, Criteria criteria) {
final TaskCompletionSource<Location> tcs = new TaskCompletionSource<>();
final Capture<ScheduledFuture<?>> timeoutFuture = new Capture<>();
final LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
final LocationListener listener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (location == null) {
return;
}
timeoutFuture.get().cancel(true);
tcs.trySetResult(location);
manager.removeUpdates(this);
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
};
timeoutFuture.set(ParseExecutors.scheduled().schedule(() -> {
tcs.trySetError(new ParseException(ParseException.TIMEOUT, "Location fetch timed out."));
manager.removeUpdates(listener);
}, timeout, TimeUnit.MILLISECONDS));
String provider = manager.getBestProvider(criteria, true);
if (provider != null) {
manager.requestLocationUpdates(provider, /* minTime */
0, /* minDistance */
0.0f, listener);
}
if (fakeLocation != null) {
listener.onLocationChanged(fakeLocation);
}
return tcs.getTask();
}
use of com.parse.boltsinternal.Capture in project Parse-SDK-Android by ParsePlatform.
the class ParsePushTest method testSubscribeInBackgroundWithCallbackSuccess.
@Test
public void testSubscribeInBackgroundWithCallbackSuccess() throws Exception {
final ParsePushChannelsController controller = mock(ParsePushChannelsController.class);
when(controller.subscribeInBackground(anyString())).thenReturn(Task.<Void>forResult(null));
ParseCorePlugins.getInstance().registerPushChannelsController(controller);
ParsePush push = new ParsePush();
final Semaphore done = new Semaphore(0);
final Capture<Exception> exceptionCapture = new Capture<>();
ParsePush.subscribeInBackground("test", e -> {
exceptionCapture.set(e);
done.release();
});
shadowMainLooper().idle();
assertNull(exceptionCapture.get());
assertTrue(done.tryAcquire(1, 10, TimeUnit.SECONDS));
verify(controller, times(1)).subscribeInBackground("test");
}
use of com.parse.boltsinternal.Capture in project Parse-SDK-Android by ParsePlatform.
the class ParsePushTest method testUnsubscribeInBackgroundWithCallbackSuccess.
@Test
public void testUnsubscribeInBackgroundWithCallbackSuccess() throws Exception {
final ParsePushChannelsController controller = mock(ParsePushChannelsController.class);
when(controller.unsubscribeInBackground(anyString())).thenReturn(Task.<Void>forResult(null));
ParseCorePlugins.getInstance().registerPushChannelsController(controller);
final Semaphore done = new Semaphore(0);
final Capture<Exception> exceptionCapture = new Capture<>();
ParsePush.unsubscribeInBackground("test", e -> {
exceptionCapture.set(e);
done.release();
});
shadowMainLooper().idle();
assertNull(exceptionCapture.get());
assertTrue(done.tryAcquire(1, 10, TimeUnit.SECONDS));
verify(controller, times(1)).unsubscribeInBackground("test");
}
use of com.parse.boltsinternal.Capture in project Parse-SDK-Android by ParsePlatform.
the class ParsePushTest method testSubscribeInBackgroundWithCallbackFail.
@Test
public void testSubscribeInBackgroundWithCallbackFail() throws Exception {
ParsePushChannelsController controller = mock(ParsePushChannelsController.class);
final ParseException exception = new ParseException(ParseException.OTHER_CAUSE, "error");
when(controller.subscribeInBackground(anyString())).thenReturn(Task.<Void>forError(exception));
ParseCorePlugins.getInstance().registerPushChannelsController(controller);
ParsePush push = new ParsePush();
final Semaphore done = new Semaphore(0);
final Capture<Exception> exceptionCapture = new Capture<>();
ParsePush.subscribeInBackground("test", e -> {
exceptionCapture.set(e);
done.release();
});
shadowMainLooper().idle();
assertSame(exception, exceptionCapture.get());
assertTrue(done.tryAcquire(1, 10, TimeUnit.SECONDS));
verify(controller, times(1)).subscribeInBackground("test");
}
Aggregations