diff --git a/README.md b/README.md index cd86d5e..8e634c9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This repository started as a personal usage of [Nick Moskalenko](https://github. ```groovy dependencies { - compile 'com.github.FrangSierra:RxFirebase:1.5.6' + implementation "com.github.FrangSierra:RxFirebase:1.5.6" } ``` ``` @@ -192,6 +192,15 @@ public static Completable updateEmail(@NonNull final FirebaseUser firebaseUser, `RxCompletableHandler` manages the CompletableEmitters in the same way that `RxHandler` manages the `Subscriber`. You can check all the differences between RxJava and RxJava 2.0 in the next [Link](https://github.com/ReactiveX/RxJava/wiki/What's-different-in-2.0) +### Plugins + +```java + public static boolean allowMainThreadQueries = false; +``` +The flag, that controls can be extensions be called on the main thread or not. +By default it doesn't permit the main thread queries + + ## License MIT License diff --git a/app/build.gradle b/app/build.gradle index 91e0db9..349e355 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,50 +1,44 @@ buildscript { ext { - rx_version = "2.1.10" + rx_version = "2.2.20" rx_android_version = "2.1.1" - firebase_auth_version = '19.2.0' - firebase_database_version = '19.2.1' - firebase_storage_version = '19.1.1' - firebase_firestore_version = '21.4.1' - firebase_functions_version = '19.0.2' - firebase_remote_version = '19.1.2' + firebase_auth_version = "21.1.0" + firebase_database_version = "20.1.0" + firebase_storage_version = "20.1.0" + firebase_firestore_version = "24.4.1" + firebase_functions_version = "20.2.1" + firebase_remote_version = "21.2.0" support_version = "28.0.3" } } + apply plugin: 'com.android.library' android { - packagingOptions { - exclude 'META-INF/LICENSE' - exclude 'META-INF/LICENSE-FIREBASE.txt' - exclude 'META-INF/NOTICE' - } - - compileSdkVersion 28 - buildToolsVersion "28.0.3" + compileSdkVersion 31 defaultConfig { minSdkVersion 14 - targetSdkVersion 28 + targetSdkVersion 31 versionCode 6 versionName "1.5.5" consumerProguardFiles 'consumer-proguard-rules.pro' } } -repositories{ +repositories { google() } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - compileOnly "com.google.firebase:firebase-auth:$firebase_auth_version" - compileOnly "com.google.firebase:firebase-database:$firebase_database_version" - compileOnly "com.google.firebase:firebase-storage:$firebase_storage_version" - compileOnly "com.google.firebase:firebase-firestore:$firebase_firestore_version" - compileOnly "com.google.firebase:firebase-functions:$firebase_functions_version" - compileOnly "com.google.firebase:firebase-config:$firebase_remote_version" + implementation "com.google.firebase:firebase-auth:$firebase_auth_version" + implementation "com.google.firebase:firebase-database:$firebase_database_version" + implementation "com.google.firebase:firebase-storage:$firebase_storage_version" + implementation "com.google.firebase:firebase-firestore:$firebase_firestore_version" + implementation "com.google.firebase:firebase-functions:$firebase_functions_version" + implementation "com.google.firebase:firebase-config:$firebase_remote_version" implementation "io.reactivex.rxjava2:rxjava:$rx_version" implementation "io.reactivex.rxjava2:rxandroid:$rx_android_version" @@ -54,6 +48,6 @@ dependencies { testImplementation "com.google.firebase:firebase-storage:$firebase_storage_version" testImplementation "com.google.firebase:firebase-firestore:$firebase_firestore_version" testImplementation "com.google.firebase:firebase-config:$firebase_remote_version" - testImplementation 'junit:junit:4.13' - testImplementation "org.mockito:mockito-core:3.3.1" + testImplementation 'junit:junit:4.13.2' + testImplementation "org.mockito:mockito-core:4.0.0" } diff --git a/app/src/main/java/durdinapps/rxfirebase2/DataSnapshotMapper.java b/app/src/main/java/durdinapps/rxfirebase2/DataSnapshotMapper.java index 6009501..180f879 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/DataSnapshotMapper.java +++ b/app/src/main/java/durdinapps/rxfirebase2/DataSnapshotMapper.java @@ -7,7 +7,6 @@ import java.util.LinkedHashMap; import java.util.List; -import androidx.annotation.NonNull; import durdinapps.rxfirebase2.exceptions.RxFirebaseDataCastException; import io.reactivex.exceptions.Exceptions; import io.reactivex.functions.Function; @@ -158,10 +157,5 @@ public RxFirebaseChildEvent apply(final RxFirebaseChildEvent rx } } - static final Predicate DATA_SNAPSHOT_EXISTENCE_PREDICATE = new Predicate() { - @Override - public boolean test(@NonNull DataSnapshot dataSnapshot) throws Exception { - return dataSnapshot.exists(); - } - }; + static final Predicate DATA_SNAPSHOT_EXISTENCE_PREDICATE = DataSnapshot::exists; } \ No newline at end of file diff --git a/app/src/main/java/durdinapps/rxfirebase2/DocumentSnapshotMapper.java b/app/src/main/java/durdinapps/rxfirebase2/DocumentSnapshotMapper.java index 90d7c2a..5afef45 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/DocumentSnapshotMapper.java +++ b/app/src/main/java/durdinapps/rxfirebase2/DocumentSnapshotMapper.java @@ -1,6 +1,5 @@ package durdinapps.rxfirebase2; - import com.google.firebase.firestore.DocumentSnapshot; import com.google.firebase.firestore.QuerySnapshot; @@ -8,7 +7,6 @@ import java.util.LinkedHashMap; import java.util.List; -import androidx.annotation.NonNull; import io.reactivex.functions.Function; import io.reactivex.functions.Predicate; @@ -96,17 +94,7 @@ public LinkedHashMap apply(final QuerySnapshot querySnapshot) { } } - static final Predicate QUERY_EXISTENCE_PREDICATE = new Predicate() { - @Override - public boolean test(@NonNull QuerySnapshot querySnapshot) throws Exception { - return !querySnapshot.isEmpty(); - } - }; + static final Predicate QUERY_EXISTENCE_PREDICATE = querySnapshot -> !querySnapshot.isEmpty(); - static final Predicate DOCUMENT_EXISTENCE_PREDICATE = new Predicate() { - @Override - public boolean test(@NonNull DocumentSnapshot documentSnapshot) throws Exception { - return documentSnapshot.exists(); - } - }; + static final Predicate DOCUMENT_EXISTENCE_PREDICATE = DocumentSnapshot::exists; } diff --git a/app/src/main/java/durdinapps/rxfirebase2/Plugins.java b/app/src/main/java/durdinapps/rxfirebase2/Plugins.java new file mode 100644 index 0000000..f128c69 --- /dev/null +++ b/app/src/main/java/durdinapps/rxfirebase2/Plugins.java @@ -0,0 +1,25 @@ +package durdinapps.rxfirebase2; + +import android.os.Looper; + +/** + * Utility class to make behavior of extensions more flexible and testable + */ +public class Plugins { + + /** + * The flag, that controls can be extensions be called on the main thread or not + * @apiNote some extensions not supported this feature, so see documentation + */ + public static boolean allowMainThreadQueries = false; + + /** + * Throws an error if you make network call to Firebase on the main thread + * @throws IllegalStateException in case of calling on the main thread + */ + public static void throwExceptionIfMainThread() { + if (!allowMainThreadQueries && Looper.myLooper() == Looper.getMainLooper()) { + throw new IllegalStateException("Network on main thread is not permitted"); + } + } +} diff --git a/app/src/main/java/durdinapps/rxfirebase2/RxCompletableHandler.java b/app/src/main/java/durdinapps/rxfirebase2/RxCompletableHandler.java index 3127275..1890b4e 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/RxCompletableHandler.java +++ b/app/src/main/java/durdinapps/rxfirebase2/RxCompletableHandler.java @@ -9,8 +9,7 @@ import io.reactivex.CompletableEmitter; - -public class RxCompletableHandler implements OnFailureListener, OnSuccessListener, OnCompleteListener { +public class RxCompletableHandler implements OnFailureListener, OnSuccessListener, OnCompleteListener { private final CompletableEmitter completableEmitter; @@ -19,7 +18,7 @@ private RxCompletableHandler(CompletableEmitter completableEmitter) { } public static void assignOnTask(CompletableEmitter completableEmitter, Task task) { - RxCompletableHandler handler = new RxCompletableHandler(completableEmitter); + RxCompletableHandler handler = new RxCompletableHandler<>(completableEmitter); task.addOnFailureListener(handler); task.addOnSuccessListener(handler); try { @@ -29,7 +28,6 @@ public static void assignOnTask(CompletableEmitter completableEmitter, Task< } } - @Override public void onFailure(@NonNull Exception e) { if (!completableEmitter.isDisposed()) diff --git a/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseAuth.java b/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseAuth.java index 8cd023a..c4b9d7e 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseAuth.java +++ b/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseAuth.java @@ -1,5 +1,7 @@ package durdinapps.rxfirebase2; +import static durdinapps.rxfirebase2.Plugins.throwExceptionIfMainThread; + import androidx.annotation.NonNull; import com.google.firebase.auth.ActionCodeResult; @@ -16,9 +18,6 @@ import io.reactivex.MaybeEmitter; import io.reactivex.MaybeOnSubscribe; import io.reactivex.Observable; -import io.reactivex.ObservableEmitter; -import io.reactivex.ObservableOnSubscribe; -import io.reactivex.functions.Cancellable; public class RxFirebaseAuth { @@ -205,27 +204,19 @@ public void subscribe(CompletableEmitter emitter) throws Exception { * * @param firebaseAuth firebaseAuth instance. * @return an {@link Observable} which emits every time that the {@link FirebaseAuth} state change. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Observable observeAuthState(@NonNull final FirebaseAuth firebaseAuth) { + return Observable.create(emitter -> { + throwExceptionIfMainThread(); - return Observable.create(new ObservableOnSubscribe() { - @Override - public void subscribe(final ObservableEmitter emitter) throws Exception { - final FirebaseAuth.AuthStateListener authStateListener = new FirebaseAuth.AuthStateListener() { - @Override - public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { - emitter.onNext(firebaseAuth); - } - }; - firebaseAuth.addAuthStateListener(authStateListener); - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - firebaseAuth.removeAuthStateListener(authStateListener); - } - }); - } + final FirebaseAuth.AuthStateListener authStateListener = emitter::onNext; + + firebaseAuth.addAuthStateListener(authStateListener); + + emitter.setCancellable(() -> firebaseAuth.removeAuthStateListener(authStateListener)); }); } diff --git a/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseChildEvent.java b/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseChildEvent.java index 4427be4..d26c784 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseChildEvent.java +++ b/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseChildEvent.java @@ -1,13 +1,12 @@ package durdinapps.rxfirebase2; - import androidx.annotation.NonNull; public class RxFirebaseChildEvent { - private EventType eventType; - private String key; - private T value; + private final EventType eventType; + private final String key; + private final T value; private String previousChildName; public RxFirebaseChildEvent(@NonNull String key, diff --git a/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseDatabase.java b/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseDatabase.java index 3b476cd..17b569f 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseDatabase.java +++ b/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseDatabase.java @@ -1,5 +1,8 @@ package durdinapps.rxfirebase2; +import static durdinapps.rxfirebase2.DataSnapshotMapper.DATA_SNAPSHOT_EXISTENCE_PREDICATE; +import static durdinapps.rxfirebase2.Plugins.throwExceptionIfMainThread; + import androidx.annotation.NonNull; import com.google.android.gms.tasks.OnFailureListener; @@ -15,6 +18,7 @@ import java.util.Iterator; import java.util.Map; +import java.util.Objects; import durdinapps.rxfirebase2.exceptions.RxFirebaseDataException; import io.reactivex.BackpressureStrategy; @@ -22,20 +26,10 @@ import io.reactivex.CompletableEmitter; import io.reactivex.CompletableOnSubscribe; import io.reactivex.Flowable; -import io.reactivex.FlowableEmitter; -import io.reactivex.FlowableOnSubscribe; import io.reactivex.Maybe; -import io.reactivex.MaybeEmitter; -import io.reactivex.MaybeOnSubscribe; -import io.reactivex.MaybeSource; import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.functions.Cancellable; import io.reactivex.functions.Function; -import static durdinapps.rxfirebase2.DataSnapshotMapper.DATA_SNAPSHOT_EXISTENCE_PREDICATE; - public class RxFirebaseDatabase { /** @@ -44,33 +38,30 @@ public class RxFirebaseDatabase { * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @param strategy {@link BackpressureStrategy} associated to this {@link Flowable} * @return a {@link Flowable} which emits when a value of the database change in the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable observeValueEvent(@NonNull final Query query, @NonNull BackpressureStrategy strategy) { - return Flowable.create(new FlowableOnSubscribe() { - @Override - public void subscribe(final FlowableEmitter emitter) throws Exception { - final ValueEventListener valueEventListener = new ValueEventListener() { - @Override - public void onDataChange(DataSnapshot dataSnapshot) { - emitter.onNext(dataSnapshot); - } + return Flowable.create(emitter -> { + throwExceptionIfMainThread(); - @Override - public void onCancelled(final DatabaseError error) { - if (!emitter.isCancelled()) - emitter.onError(new RxFirebaseDataException(error)); - } - }; - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - query.removeEventListener(valueEventListener); - } - }); - query.addValueEventListener(valueEventListener); - } + final ValueEventListener valueEventListener = new ValueEventListener() { + @Override + public void onDataChange(@NonNull DataSnapshot dataSnapshot) { + emitter.onNext(dataSnapshot); + } + + @Override + public void onCancelled(@NonNull final DatabaseError error) { + if (!emitter.isCancelled()) + emitter.onError(new RxFirebaseDataException(error)); + } + }; + + emitter.setCancellable(() -> query.removeEventListener(valueEventListener)); + query.addValueEventListener(valueEventListener); }, strategy); } @@ -80,29 +71,30 @@ public void cancel() throws Exception { * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @return a {@link Maybe} which emits the actual state of the database for the given query. onSuccess will be only call when * the given {@link DataSnapshot} exists onComplete will only called when the data doesn't exist. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Maybe observeSingleValueEvent(@NonNull final Query query) { - return Maybe.create(new MaybeOnSubscribe() { - @Override - public void subscribe(final MaybeEmitter emitter) throws Exception { - query.addListenerForSingleValueEvent(new ValueEventListener() { - @Override - public void onDataChange(DataSnapshot dataSnapshot) { - if (dataSnapshot.exists()) { - emitter.onSuccess(dataSnapshot); - } else { - emitter.onComplete(); - } - } + return Maybe.create(emitter -> { + throwExceptionIfMainThread(); - @Override - public void onCancelled(DatabaseError error) { - if (!emitter.isDisposed()) - emitter.onError(new RxFirebaseDataException(error)); + query.addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + if (dataSnapshot.exists()) { + emitter.onSuccess(dataSnapshot); + } else { + emitter.onComplete(); } - }); - } + } + + @Override + public void onCancelled(DatabaseError error) { + if (!emitter.isDisposed()) + emitter.onError(new RxFirebaseDataException(error)); + } + }); }); } @@ -113,36 +105,38 @@ public void onCancelled(DatabaseError error) { * @param fireLocalEvents boolean which allow to receive calls of your transaction in your local device. * @param transactionValue value of the transaction. * @return a {@link Single} which emits the final {@link DataSnapshot} value if the transaction success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single runTransaction(@NonNull final DatabaseReference ref, - @NonNull final boolean fireLocalEvents, - @NonNull final long transactionValue) { - return Single.create(new SingleOnSubscribe() { - @Override - public void subscribe(final SingleEmitter emitter) throws Exception { - ref.runTransaction(new Transaction.Handler() { - @Override - public Transaction.Result doTransaction(MutableData mutableData) { - Integer currentValue = mutableData.getValue(Integer.class); - if (currentValue == null) { - mutableData.setValue(transactionValue); - } else { - mutableData.setValue(currentValue + transactionValue); - } - return Transaction.success(mutableData); + final boolean fireLocalEvents, + final long transactionValue) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + ref.runTransaction(new Transaction.Handler() { + @NonNull + @Override + public Transaction.Result doTransaction(@NonNull MutableData mutableData) { + Integer currentValue = mutableData.getValue(Integer.class); + if (currentValue == null) { + mutableData.setValue(transactionValue); + } else { + mutableData.setValue(currentValue + transactionValue); } + return Transaction.success(mutableData); + } - @Override - public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) { - if (databaseError != null && !emitter.isDisposed()) { - emitter.onError(new RxFirebaseDataException(databaseError)); - } else { - emitter.onSuccess(dataSnapshot); - } + @Override + public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) { + if (databaseError != null && !emitter.isDisposed()) { + emitter.onError(new RxFirebaseDataException(databaseError)); + } else { + emitter.onSuccess(dataSnapshot); } - }, fireLocalEvents); - } + } + }, fireLocalEvents); }); } @@ -209,57 +203,54 @@ public void onComplete(DatabaseError error, DatabaseReference databaseReference) * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @param strategy {@link BackpressureStrategy} associated to this {@link Flowable} * @return a {@link Flowable} which emits when a value of a child int the database change on the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable> observeChildEvent( @NonNull final Query query, @NonNull BackpressureStrategy strategy) { - return Flowable.create(new FlowableOnSubscribe>() { - @Override - public void subscribe(final FlowableEmitter> emitter) throws Exception { - final ChildEventListener childEventListener = new ChildEventListener() { + return Flowable.create(emitter -> { + throwExceptionIfMainThread(); - @Override - public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) { - emitter.onNext( - new RxFirebaseChildEvent<>(dataSnapshot.getKey(), dataSnapshot, previousChildName, - RxFirebaseChildEvent.EventType.ADDED)); - } + final ChildEventListener childEventListener = new ChildEventListener() { - @Override - public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) { - emitter.onNext( - new RxFirebaseChildEvent<>(dataSnapshot.getKey(), dataSnapshot, previousChildName, - RxFirebaseChildEvent.EventType.CHANGED)); - } + @Override + public void onChildAdded(@NonNull DataSnapshot dataSnapshot, String previousChildName) { + emitter.onNext( + new RxFirebaseChildEvent<>(Objects.requireNonNull(dataSnapshot.getKey()), dataSnapshot, previousChildName, + RxFirebaseChildEvent.EventType.ADDED)); + } - @Override - public void onChildRemoved(DataSnapshot dataSnapshot) { - emitter.onNext(new RxFirebaseChildEvent<>(dataSnapshot.getKey(), dataSnapshot, - RxFirebaseChildEvent.EventType.REMOVED)); - } + @Override + public void onChildChanged(@NonNull DataSnapshot dataSnapshot, String previousChildName) { + emitter.onNext( + new RxFirebaseChildEvent<>(Objects.requireNonNull(dataSnapshot.getKey()), dataSnapshot, previousChildName, + RxFirebaseChildEvent.EventType.CHANGED)); + } - @Override - public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) { - emitter.onNext( - new RxFirebaseChildEvent<>(dataSnapshot.getKey(), dataSnapshot, previousChildName, - RxFirebaseChildEvent.EventType.MOVED)); - } + @Override + public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) { + emitter.onNext(new RxFirebaseChildEvent<>(Objects.requireNonNull(dataSnapshot.getKey()), dataSnapshot, + RxFirebaseChildEvent.EventType.REMOVED)); + } - @Override - public void onCancelled(DatabaseError error) { - if (!emitter.isCancelled()) - emitter.onError(new RxFirebaseDataException(error)); - } - }; - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - query.removeEventListener(childEventListener); - } - }); - query.addChildEventListener(childEventListener); + @Override + public void onChildMoved(@NonNull DataSnapshot dataSnapshot, String previousChildName) { + emitter.onNext( + new RxFirebaseChildEvent<>(Objects.requireNonNull(dataSnapshot.getKey()), dataSnapshot, previousChildName, + RxFirebaseChildEvent.EventType.MOVED)); + } - } + @Override + public void onCancelled(@NonNull DatabaseError error) { + if (!emitter.isCancelled()) + emitter.onError(new RxFirebaseDataException(error)); + } + }; + + emitter.setCancellable(() -> query.removeEventListener(childEventListener)); + + query.addChildEventListener(childEventListener); }, strategy); } @@ -268,17 +259,13 @@ public void cancel() throws Exception { * * @param whereRefs array of {@link DatabaseReference references.} * @return a {@link Flowable} which emmit {@link DataSnapshot} from the given queries. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable observeMultipleSingleValueEvent(@NonNull DatabaseReference... whereRefs) { return Maybe.merge(Flowable.fromArray(whereRefs) - .map(new Function>() { - @Override - public MaybeSource apply(@NonNull DatabaseReference databaseReference) throws - Exception { - return observeSingleValueEvent(databaseReference); - } - }) + .map(RxFirebaseDatabase::observeSingleValueEvent) ); } @@ -302,17 +289,14 @@ public MaybeSource apply(@NonNull DatabaseReference data @NonNull public static Maybe requestFilteredReferenceKeys(@NonNull final DatabaseReference from, @NonNull Query whereRef) { - return observeSingleValueEvent(whereRef, new Function() { - @Override - public DatabaseReference[] apply(@NonNull DataSnapshot dataSnapshot) throws Exception { - int childrenCount = (int) dataSnapshot.getChildrenCount(); - DatabaseReference[] filterRefs = new DatabaseReference[childrenCount]; - final Iterator iterator = dataSnapshot.getChildren().iterator(); - for (int i = 0; i < childrenCount; i++) { - filterRefs[i] = from.child(iterator.next().getKey()); - } - return filterRefs; + return observeSingleValueEvent(whereRef, dataSnapshot -> { + int childrenCount = (int) dataSnapshot.getChildrenCount(); + DatabaseReference[] filterRefs = new DatabaseReference[childrenCount]; + final Iterator iterator = dataSnapshot.getChildren().iterator(); + for (int i = 0; i < childrenCount; i++) { + filterRefs[i] = from.child(Objects.requireNonNull(iterator.next().getKey())); } + return filterRefs; }); } @@ -323,6 +307,8 @@ public DatabaseReference[] apply(@NonNull DataSnapshot dataSnapshot) throws Exce * @param clazz class type for the {@link DataSnapshot} items. * @param strategy {@link BackpressureStrategy} associated to this {@link Flowable} * @return a {@link Flowable} which emits when a value of the database change in the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable observeValueEvent(@NonNull final Query query, @@ -351,6 +337,8 @@ public static Maybe observeSingleValueEvent(@NonNull final Query query, * @param clazz class type for the {@link DataSnapshot} items. * @param strategy {@link BackpressureStrategy} associated to this {@link Flowable} * @return a {@link Flowable} which emits when a value of a child int the database change on the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable> observeChildEvent( @@ -366,6 +354,8 @@ public static Flowable> observeChildEvent( * @param mapper specific function to map the dispatched events. * @param strategy {@link BackpressureStrategy} associated to this {@link Flowable} * @return a {@link Flowable} which emits when a value of the database change in the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable observeValueEvent(@NonNull final Query query, @@ -380,10 +370,13 @@ public static Flowable observeValueEvent(@NonNull final Query query, * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @param mapper specific function to map the dispatched events. * @return a {@link Maybe} which emits the actual state of the database for the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull - public static Maybe observeSingleValueEvent(@NonNull final Query query, - @NonNull final Function mapper) { + public static Maybe observeSingleValueEvent( + @NonNull final Query query, + @NonNull final Function mapper) { return observeSingleValueEvent(query) .filter(DATA_SNAPSHOT_EXISTENCE_PREDICATE) .map(mapper); @@ -396,6 +389,8 @@ public static Maybe observeSingleValueEvent(@NonNull final Query query, * @param mapper specific function to map the dispatched events. * @param strategy {@link BackpressureStrategy} associated to this {@link Flowable} * @return a {@link Flowable} which emits when a value of a child int the database change on the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable> observeChildEvent( @@ -409,6 +404,8 @@ public static Flowable> observeChildEvent( * * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @return a {@link Flowable} which emits when a value of the database change in the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable observeValueEvent(@NonNull final Query query) { @@ -421,6 +418,8 @@ public static Flowable observeValueEvent(@NonNull final Query quer * * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @return a {@link Flowable} which emits when a value of a child int the database change on the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable> observeChildEvent( @@ -434,10 +433,12 @@ public static Flowable> observeChildEvent( * @param ref reference represents a particular location in your database. * @param transactionValue value of the transaction. * @return a {@link Single} which emits the final {@link DataSnapshot} value if the transaction success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single runTransaction(@NonNull final DatabaseReference ref, - @NonNull final long transactionValue) { + final long transactionValue) { return runTransaction(ref, true, transactionValue); } @@ -447,6 +448,8 @@ public static Single runTransaction(@NonNull final DatabaseReferen * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @param clazz class type for the {@link DataSnapshot} items. * @return a {@link Flowable} which emits when a value of the database change in the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable observeValueEvent(@NonNull final Query query, @@ -460,6 +463,8 @@ public static Flowable observeValueEvent(@NonNull final Query query, * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @param clazz class type for the {@link DataSnapshot} items. * @return a {@link Flowable} which emits when a value of a child int the database change on the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable> observeChildEvent( @@ -472,10 +477,13 @@ public static Flowable> observeChildEvent( * * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @return a {@link Flowable} which emits when a value of the database change in the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull - public static Flowable observeValueEvent(@NonNull final Query query, - @NonNull final Function mapper) { + public static Flowable observeValueEvent( + @NonNull final Query query, + @NonNull final Function mapper) { return observeValueEvent(query, BackpressureStrategy.DROP).map(mapper); } @@ -485,6 +493,8 @@ public static Flowable observeValueEvent(@NonNull final Query query, * @param query reference represents a particular location in your Database and can be used for reading or writing data to that Database location. * @param mapper specific function to map the dispatched events. * @return a {@link Flowable} which emits when a value of a child int the database change on the given query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable> observeChildEvent( diff --git a/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseStorage.java b/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseStorage.java index 12be138..417108a 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseStorage.java +++ b/app/src/main/java/durdinapps/rxfirebase2/RxFirebaseStorage.java @@ -1,10 +1,11 @@ package durdinapps.rxfirebase2; +import static durdinapps.rxfirebase2.Plugins.throwExceptionIfMainThread; + import android.net.Uri; + import androidx.annotation.NonNull; -import com.google.android.gms.tasks.OnFailureListener; -import com.google.android.gms.tasks.OnSuccessListener; import com.google.firebase.storage.FileDownloadTask; import com.google.firebase.storage.StorageMetadata; import com.google.firebase.storage.StorageReference; @@ -22,9 +23,6 @@ import io.reactivex.MaybeEmitter; import io.reactivex.MaybeOnSubscribe; import io.reactivex.Single; -import io.reactivex.SingleEmitter; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.functions.Cancellable; public class RxFirebaseStorage { @@ -69,33 +67,24 @@ public void subscribe(MaybeEmitter emitter) throws Exception { * @param storageRef represents a reference to a Google Cloud Storage object. * @param destinationFile a File representing the path the object should be downloaded to. * @return a {@link Single} which emits an {@link FileDownloadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single getFile(@NonNull final StorageReference storageRef, @NonNull final File destinationFile) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.getFile(destinationFile).addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.getFile(destinationFile) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -105,33 +94,24 @@ public void cancel() throws Exception { * @param storageRef represents a reference to a Google Cloud Storage object. * @param destinationUri a file system URI representing the path the object should be downloaded to. * @return a {@link Single} which emits an {@link FileDownloadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single getFile(@NonNull final StorageReference storageRef, @NonNull final Uri destinationUri) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.getFile(destinationUri).addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.getFile(destinationUri) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -156,32 +136,23 @@ public void subscribe(MaybeEmitter emitter) throws Exception { * * @param storageRef represents a reference to a Google Cloud Storage object. * @return a {@link Single} which emits an {@link StreamDownloadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single getStream(@NonNull final StorageReference storageRef) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.getStream().addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.getStream() + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -193,33 +164,24 @@ public void cancel() throws Exception { * The StreamDownloadTask.StreamProcessor is called on a background thread and checked exceptions thrown * from this object will be returned as a failure to the OnFailureListener registered on the StreamDownloadTask. * @return a {@link Single} which emits an {@link StreamDownloadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single getStream(@NonNull final StorageReference storageRef, @NonNull final StreamDownloadTask.StreamProcessor processor) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.getStream(processor).addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(StreamDownloadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.getStream(processor) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -229,32 +191,24 @@ public void cancel() throws Exception { * @param storageRef represents a reference to a Google Cloud Storage object. * @param bytes The byte[] to upload. * @return a {@link Single} which emits an {@link UploadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single putBytes(@NonNull final StorageReference storageRef, @NonNull final byte[] bytes) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.putBytes(bytes).addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.putBytes(bytes) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -265,34 +219,25 @@ public void cancel() throws Exception { * @param bytes The byte[] to upload. * @param metadata {@link StorageMetadata} containing additional information (MIME type, etc.) about the object being uploaded. * @return a {@link Single} which emits an {@link UploadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single putBytes(@NonNull final StorageReference storageRef, @NonNull final byte[] bytes, @NonNull final StorageMetadata metadata) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.putBytes(bytes, metadata).addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.putBytes(bytes, metadata) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -302,33 +247,24 @@ public void cancel() throws Exception { * @param storageRef represents a reference to a Google Cloud Storage object. * @param uri The source of the upload. This can be a file:// scheme or any content URI. A content resolver will be used to load the data. * @return a {@link Single} which emits an {@link UploadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single putFile(@NonNull final StorageReference storageRef, @NonNull final Uri uri) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.putFile(uri).addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.putFile(uri) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -339,35 +275,25 @@ public void cancel() throws Exception { * @param uri The source of the upload. This can be a file:// scheme or any content URI. A content resolver will be used to load the data. * @param metadata {@link StorageMetadata} containing additional information (MIME type, etc.) about the object being uploaded. * @return a {@link Single} which emits an {@link UploadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single putFile(@NonNull final StorageReference storageRef, @NonNull final Uri uri, @NonNull final StorageMetadata metadata) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = storageRef.putFile(uri, metadata) - .addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -379,36 +305,26 @@ public void cancel() throws Exception { * @param metadata {@link StorageMetadata} containing additional information (MIME type, etc.) about the object being uploaded. * @param existingUploadUri If set, an attempt is made to resume an existing upload session as defined by getUploadSessionUri(). * @return a {@link Single} which emits an {@link UploadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single putFile(@NonNull final StorageReference storageRef, @NonNull final Uri uri, @NonNull final StorageMetadata metadata, @NonNull final Uri existingUploadUri) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.putFile(uri, metadata, existingUploadUri) - .addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.putFile(uri, metadata, existingUploadUri) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -417,35 +333,25 @@ public void cancel() throws Exception { * @param stream The InputStream to upload. * @param metadata {@link StorageMetadata} containing additional information (MIME type, etc.) about the object being uploaded. * @return a {@link Single} which emits an {@link UploadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single putStream(@NonNull final StorageReference storageRef, @NonNull final InputStream stream, @NonNull final StorageMetadata metadata) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.putStream(stream, metadata) - .addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.putStream(stream, metadata) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } @@ -455,33 +361,24 @@ public void cancel() throws Exception { * @param storageRef represents a reference to a Google Cloud Storage object. * @param stream The InputStream to upload. * @return a {@link Single} which emits an {@link UploadTask.TaskSnapshot} if success. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Single putStream(@NonNull final StorageReference storageRef, @NonNull final InputStream stream) { - return Single.create(new SingleOnSubscribe() { - public void subscribe(final SingleEmitter emitter) throws Exception { - final StorageTask taskSnapshotStorageTask = - storageRef.putStream(stream).addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { - emitter.onSuccess(taskSnapshot); - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { + return Single.create(emitter -> { + throwExceptionIfMainThread(); + + final StorageTask taskSnapshotStorageTask = + storageRef.putStream(stream) + .addOnSuccessListener(emitter::onSuccess) + .addOnFailureListener(e -> { if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - taskSnapshotStorageTask.cancel(); - } - }); - } + emitter.onError(e); + }); + + emitter.setCancellable(taskSnapshotStorageTask::cancel); }); } diff --git a/app/src/main/java/durdinapps/rxfirebase2/RxFirestore.java b/app/src/main/java/durdinapps/rxfirebase2/RxFirestore.java index 9864776..a377623 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/RxFirestore.java +++ b/app/src/main/java/durdinapps/rxfirebase2/RxFirestore.java @@ -1,7 +1,11 @@ package durdinapps.rxfirebase2; +import static durdinapps.rxfirebase2.DocumentSnapshotMapper.DOCUMENT_EXISTENCE_PREDICATE; +import static durdinapps.rxfirebase2.DocumentSnapshotMapper.QUERY_EXISTENCE_PREDICATE; +import static durdinapps.rxfirebase2.Plugins.throwExceptionIfMainThread; import android.app.Activity; + import androidx.annotation.NonNull; import com.google.android.gms.tasks.OnCompleteListener; @@ -37,8 +41,6 @@ import io.reactivex.FlowableEmitter; import io.reactivex.FlowableOnSubscribe; import io.reactivex.Maybe; -import io.reactivex.MaybeEmitter; -import io.reactivex.MaybeOnSubscribe; import io.reactivex.Single; import io.reactivex.SingleEmitter; import io.reactivex.SingleOnSubscribe; @@ -46,9 +48,6 @@ import io.reactivex.functions.Function; import io.reactivex.schedulers.Schedulers; -import static durdinapps.rxfirebase2.DocumentSnapshotMapper.DOCUMENT_EXISTENCE_PREDICATE; -import static durdinapps.rxfirebase2.DocumentSnapshotMapper.QUERY_EXISTENCE_PREDICATE; - public class RxFirestore { /** @@ -242,15 +241,16 @@ public void subscribe(CompletableEmitter emitter) { * * @param ref The given Document reference. * @param updateFieldsMap A map of field / value pairs to update. Fields can contain dots to reference nested fields within the document. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable updateDocument(@NonNull final DocumentReference ref, @NonNull final Map updateFieldsMap) { - return Completable.create(new CompletableOnSubscribe() { - @Override - public void subscribe(CompletableEmitter emitter) { - RxCompletableHandler.assignOnTask(emitter, ref.update(updateFieldsMap)); - } + return Completable.create(emitter -> { + throwExceptionIfMainThread(); + + RxCompletableHandler.assignOnTask(emitter, ref.update(updateFieldsMap)); }); } @@ -262,6 +262,8 @@ public void subscribe(CompletableEmitter emitter) { * * @param ref The given Document reference. * @param updateFieldsMap A map of field / value pairs to update. Fields can contain dots to reference nested fields within the document. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable updateDocumentOffline(@NonNull final DocumentReference ref, @@ -301,6 +303,8 @@ public void subscribe(CompletableEmitter emitter) { * @param field The first field to update. Fields can contain dots to reference a nested field within the document. * @param value The first value * @param moreFieldsAndValues Additional field/value pairs. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable updateDocumentOffline(@NonNull final DocumentReference ref, @@ -343,6 +347,8 @@ public void subscribe(CompletableEmitter emitter) { * @param fieldPath The first field to update. Fields can contain dots to reference a nested field within the document. * @param value The first value * @param moreFieldsAndValues Additional field/value pairs. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable updateDocumentOffline(@NonNull final DocumentReference ref, @@ -400,6 +406,8 @@ public void subscribe(CompletableEmitter emitter) { * @param ref The given Document reference. * @param pojo The POJO that will be used to populate the document contents. * @param options An object to configure the set behavior. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable setDocumentOffline(@NonNull final DocumentReference ref, @@ -429,6 +437,8 @@ public static Completable setDocument(@NonNull final DocumentReference ref, * * @param ref The given Document reference. * @param setFieldsMap A map of the fields and values for the document. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable setDocumentOffline(@NonNull final DocumentReference ref, @@ -457,6 +467,8 @@ public static Completable setDocument(@NonNull final DocumentReference ref, * * @param ref The given Document reference. * @param pojo The POJO that will be used to populate the document contents. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable setDocumentOffline(@NonNull final DocumentReference ref, @@ -469,14 +481,15 @@ public static Completable setDocumentOffline(@NonNull final DocumentReference re * Deletes the document referred to by this DocumentReference. * * @param ref The given Document reference. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable deleteDocument(@NonNull final DocumentReference ref) { - return Completable.create(new CompletableOnSubscribe() { - @Override - public void subscribe(CompletableEmitter emitter) { - RxCompletableHandler.assignOnTask(emitter, ref.delete()); - } + return Completable.create(emitter -> { + throwExceptionIfMainThread(); + + RxCompletableHandler.assignOnTask(emitter, ref.delete()); }); } @@ -484,6 +497,8 @@ public void subscribe(CompletableEmitter emitter) { * Deletes the document referred to by this DocumentReference. * * @param ref The given Document reference. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Completable deleteDocumentOffline(@NonNull final DocumentReference ref) { @@ -495,29 +510,24 @@ public static Completable deleteDocumentOffline(@NonNull final DocumentReference * Reads the document referenced by this DocumentReference. * * @param ref The given Document reference. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Maybe getDocument(@NonNull final DocumentReference ref) { - return Maybe.create(new MaybeOnSubscribe() { - @Override - public void subscribe(final MaybeEmitter emitter) { - ref.get().addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(DocumentSnapshot documentSnapshot) { - if (documentSnapshot.exists()) { - emitter.onSuccess(documentSnapshot); - } else { - emitter.onComplete(); - } - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - } + return Maybe.create(emitter -> { + throwExceptionIfMainThread(); + + ref.get().addOnSuccessListener(documentSnapshot -> { + if (documentSnapshot.exists()) { + emitter.onSuccess(documentSnapshot); + } else { + emitter.onComplete(); + } + }).addOnFailureListener(e -> { + if (!emitter.isDisposed()) + emitter.onError(e); + }); }); } @@ -525,29 +535,24 @@ public void onFailure(@NonNull Exception e) { * Reads the collection referenced by this DocumentReference * * @param ref The given Collection reference. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Maybe getCollection(@NonNull final CollectionReference ref) { - return Maybe.create(new MaybeOnSubscribe() { - @Override - public void subscribe(final MaybeEmitter emitter) throws Exception { - ref.get().addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(QuerySnapshot documentSnapshots) { - if (documentSnapshots.isEmpty()) { - emitter.onComplete(); - } else { - emitter.onSuccess(documentSnapshots); - } - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - } + return Maybe.create(emitter -> { + throwExceptionIfMainThread(); + + ref.get().addOnSuccessListener(documentSnapshots -> { + if (documentSnapshots.isEmpty()) { + emitter.onComplete(); + } else { + emitter.onSuccess(documentSnapshots); + } + }).addOnFailureListener(e -> { + if (!emitter.isDisposed()) + emitter.onError(e); + }); }); } @@ -556,29 +561,24 @@ public void onFailure(@NonNull Exception e) { * Reads the collection referenced by this DocumentReference * * @param query The given Collection query. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Maybe getCollection(@NonNull final Query query) { - return Maybe.create(new MaybeOnSubscribe() { - @Override - public void subscribe(final MaybeEmitter emitter) { - query.get().addOnSuccessListener(new OnSuccessListener() { - @Override - public void onSuccess(QuerySnapshot documentSnapshots) { - if (documentSnapshots.isEmpty()) { - emitter.onComplete(); - } else { - emitter.onSuccess(documentSnapshots); - } - } - }).addOnFailureListener(new OnFailureListener() { - @Override - public void onFailure(@NonNull Exception e) { - if (!emitter.isDisposed()) - emitter.onError(e); - } - }); - } + return Maybe.create(emitter -> { + throwExceptionIfMainThread(); + + query.get().addOnSuccessListener(documentSnapshots -> { + if (documentSnapshots.isEmpty()) { + emitter.onComplete(); + } else { + emitter.onSuccess(documentSnapshots); + } + }).addOnFailureListener(e -> { + if (!emitter.isDisposed()) + emitter.onError(e); + }); }); } @@ -588,31 +588,24 @@ public void onFailure(@NonNull Exception e) { * @param ref The given Document reference. * @param metadataChanges Listen for metadata changes * @param strategy {@link BackpressureStrategy} associated to this {@link Flowable} + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Flowable observeDocumentRef(@NonNull final DocumentReference ref, @NonNull final MetadataChanges metadataChanges, @NonNull BackpressureStrategy strategy) { - return Flowable.create(new FlowableOnSubscribe() { - @Override - public void subscribe(final FlowableEmitter emitter) throws Exception { - final ListenerRegistration registration = ref.addSnapshotListener(metadataChanges, new EventListener() { - @Override - public void onEvent(DocumentSnapshot documentSnapshot, FirebaseFirestoreException e) { - if (e != null && !emitter.isCancelled()) { - emitter.onError(e); - return; - } - emitter.onNext(documentSnapshot); - } - }); - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() throws Exception { - registration.remove(); - } - }); - } + return Flowable.create(emitter -> { + throwExceptionIfMainThread(); + + final ListenerRegistration registration = ref.addSnapshotListener(metadataChanges, (documentSnapshot, e) -> { + if (e != null && !emitter.isCancelled()) { + emitter.onError(e); + return; + } + emitter.onNext(documentSnapshot); + }); + emitter.setCancellable(registration::remove); }, strategy); } @@ -1227,6 +1220,8 @@ public static Flowable observeQueryRef(@NonNull final Query ref, * * @param ref The given Collection reference. * @param clazz class type for the {@link DocumentSnapshot} items. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Maybe> getCollection(@NonNull final CollectionReference ref, @@ -1239,11 +1234,13 @@ public static Maybe> getCollection(@NonNull final CollectionReferenc * * @param ref The given Collection reference. * @param mapper specific function to map the dispatched events. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull - public static Maybe> getCollection(CollectionReference ref, - DocumentSnapshotMapper> mapper) { + public static Maybe> getCollection( + CollectionReference ref, + DocumentSnapshotMapper> mapper) { return getCollection(ref) .filter(QUERY_EXISTENCE_PREDICATE) .map(mapper); @@ -1266,11 +1263,13 @@ public static Maybe> getCollection(@NonNull final Query query, * * @param query The given Collection query. * @param mapper specific function to map the dispatched events. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull - public static Maybe> getCollection(@NonNull Query query, - @NonNull DocumentSnapshotMapper> mapper) { + public static Maybe> getCollection( + @NonNull Query query, + @NonNull DocumentSnapshotMapper> mapper) { return getCollection(query) .filter(QUERY_EXISTENCE_PREDICATE) .map(mapper); @@ -1281,6 +1280,8 @@ public static Maybe> getCollection(@NonNull Query query, * * @param ref The given Document reference. * @param clazz class type for the {@link DocumentSnapshot} items. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull public static Maybe getDocument(@NonNull final DocumentReference ref, @@ -1293,10 +1294,13 @@ public static Maybe getDocument(@NonNull final DocumentReference ref, * * @param ref The given Document reference. * @param mapper specific function to map the dispatched events. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ @NonNull - public static Maybe getDocument(@NonNull final DocumentReference ref, - @NonNull final Function mapper) { + public static Maybe getDocument( + @NonNull final DocumentReference ref, + @NonNull final Function mapper) { return getDocument(ref) .filter(DOCUMENT_EXISTENCE_PREDICATE) .map(mapper); diff --git a/app/src/main/java/durdinapps/rxfirebase2/RxFirestoreOfflineHandler.java b/app/src/main/java/durdinapps/rxfirebase2/RxFirestoreOfflineHandler.java index dbd5c67..78b1ddf 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/RxFirestoreOfflineHandler.java +++ b/app/src/main/java/durdinapps/rxfirebase2/RxFirestoreOfflineHandler.java @@ -1,15 +1,11 @@ package durdinapps.rxfirebase2; +import static durdinapps.rxfirebase2.Plugins.throwExceptionIfMainThread; + import com.google.firebase.firestore.DocumentReference; -import com.google.firebase.firestore.DocumentSnapshot; -import com.google.firebase.firestore.EventListener; -import com.google.firebase.firestore.FirebaseFirestoreException; import com.google.firebase.firestore.ListenerRegistration; -import androidx.annotation.Nullable; + import io.reactivex.Completable; -import io.reactivex.CompletableEmitter; -import io.reactivex.CompletableOnSubscribe; -import io.reactivex.functions.Cancellable; public class RxFirestoreOfflineHandler { @@ -20,36 +16,30 @@ public class RxFirestoreOfflineHandler { * * @param ref Document reference to be listened. * @return A Completable which emits when the given reference receives an offline update. + * @throws IllegalStateException if operation is happening on the main thread + * @see Plugins */ public static Completable listenOfflineListener(final DocumentReference ref) { - return Completable.create(new CompletableOnSubscribe() { - @Override - public void subscribe(final CompletableEmitter emitter) { - try { - final ListenerRegistration listener = ref.addSnapshotListener(new EventListener() { - @Override - public void onEvent(@Nullable DocumentSnapshot documentSnapshot, @Nullable FirebaseFirestoreException e) { - if (e != null) { - emitter.onError(e); + return Completable.create(emitter -> { + throwExceptionIfMainThread(); + + try { + final ListenerRegistration listener = + ref.addSnapshotListener((documentSnapshot, firebaseError) -> { + if (firebaseError != null) { + emitter.onError(firebaseError); + } else { + if (documentSnapshot != null) { + emitter.onComplete(); } else { - if (documentSnapshot != null) { - emitter.onComplete(); - } else { - emitter.onError(new NullPointerException("Empty Snapshot")); - } + emitter.onError(new NullPointerException("Empty Snapshot")); } } - }); + }); - emitter.setCancellable(new Cancellable() { - @Override - public void cancel() { - listener.remove(); - } - }); - } catch (Exception e) { - emitter.onError(e); - } + emitter.setCancellable(listener::remove); + } catch (Exception e) { + emitter.onError(e); } }); } diff --git a/app/src/main/java/durdinapps/rxfirebase2/RxHandler.java b/app/src/main/java/durdinapps/rxfirebase2/RxHandler.java index 4881dd8..364f56a 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/RxHandler.java +++ b/app/src/main/java/durdinapps/rxfirebase2/RxHandler.java @@ -20,7 +20,7 @@ private RxHandler(MaybeEmitter emitter) { } public static void assignOnTask(MaybeEmitter emitter, Task task) { - RxHandler handler = new RxHandler(emitter); + RxHandler handler = new RxHandler<>(emitter); task.addOnSuccessListener(handler); task.addOnFailureListener(handler); try { diff --git a/app/src/main/java/durdinapps/rxfirebase2/exceptions/RxFirebaseDataException.java b/app/src/main/java/durdinapps/rxfirebase2/exceptions/RxFirebaseDataException.java index 2f4f36a..2f49a34 100644 --- a/app/src/main/java/durdinapps/rxfirebase2/exceptions/RxFirebaseDataException.java +++ b/app/src/main/java/durdinapps/rxfirebase2/exceptions/RxFirebaseDataException.java @@ -16,6 +16,7 @@ public DatabaseError getError() { return error; } + @NonNull @Override public String toString() { return "RxFirebaseDataException{" + diff --git a/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseAuthTest.java b/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseAuthTest.java index 60b7825..4a2ee92 100644 --- a/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseAuthTest.java +++ b/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseAuthTest.java @@ -77,6 +77,8 @@ public class RxFirebaseAuthTest { public void setup() { MockitoAnnotations.initMocks(this); + Plugins.allowMainThreadQueries = true; + setupTask(authResultTask); setupTask(providerQueryResultTask); setupTask(actionCodeResultTask); diff --git a/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseDatabaseTest.java b/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseDatabaseTest.java index eed8dd0..c363f58 100644 --- a/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseDatabaseTest.java +++ b/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseDatabaseTest.java @@ -1,7 +1,12 @@ package durdinapps.rxfirebase2; - -import android.app.DownloadManager; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static durdinapps.rxfirebase2.RxTestUtil.ANY_KEY; +import static durdinapps.rxfirebase2.RxTestUtil.PREVIOUS_CHILD_NAME; import com.google.android.gms.tasks.Task; import com.google.firebase.database.ChildEventListener; @@ -30,14 +35,6 @@ import io.reactivex.observers.TestObserver; import io.reactivex.subscribers.TestSubscriber; -import static durdinapps.rxfirebase2.RxTestUtil.ANY_KEY; -import static durdinapps.rxfirebase2.RxTestUtil.PREVIOUS_CHILD_NAME; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - public class RxFirebaseDatabaseTest { @Mock @@ -70,6 +67,8 @@ public class RxFirebaseDatabaseTest { public void setup() { MockitoAnnotations.initMocks(this); + Plugins.allowMainThreadQueries = true; + childDataList.add(childData); childDataMap.put(ANY_KEY, childData); updatedData.put(databaseReference.toString(), childData); diff --git a/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseStorageTest.java b/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseStorageTest.java index fff5490..8ea8445 100644 --- a/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseStorageTest.java +++ b/app/src/test/java/durdinapps/rxfirebase2/RxFirebaseStorageTest.java @@ -90,6 +90,8 @@ public class RxFirebaseStorageTest { public void setup() { MockitoAnnotations.initMocks(this); + Plugins.allowMainThreadQueries = true; + setupTask(mockBytesTask); setupTask(mockVoidTask); setupTask(mockUriTask); diff --git a/app/src/test/java/durdinapps/rxfirebase2/RxFirestoreTest.java b/app/src/test/java/durdinapps/rxfirebase2/RxFirestoreTest.java index 55083b9..b2bcb9e 100644 --- a/app/src/test/java/durdinapps/rxfirebase2/RxFirestoreTest.java +++ b/app/src/test/java/durdinapps/rxfirebase2/RxFirestoreTest.java @@ -93,6 +93,8 @@ public class RxFirestoreTest { public void setup() { MockitoAnnotations.initMocks(this); + Plugins.allowMainThreadQueries = true; + setupTask(documentSnapshotTask); setupTask(emptyDocumentSnapshotTask); setupTask(queryResultTask); diff --git a/build.gradle b/build.gradle index fae5e0d..f7c23ca 100644 --- a/build.gradle +++ b/build.gradle @@ -3,10 +3,12 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } + dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' + classpath("com.android.tools.build:gradle:7.1.3") + classpath("com.google.gms:google-services:4.3.14") classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -16,7 +18,7 @@ buildscript { allprojects { repositories { google() - jcenter() + mavenCentral() } } diff --git a/gradle.properties b/gradle.properties index aac7c9b..143455e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,6 +11,9 @@ # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m +android.useAndroidX=true + +android.enableJetifier=true # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 13372ae..1948b90 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index beada93..041e9c1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Nov 30 11:51:14 CET 2018 +#Tue Dec 20 21:30:19 GMT+02:00 2022 distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 9d82f78..cccdd3d 100755 --- a/gradlew +++ b/gradlew @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh ############################################################################## ## @@ -6,20 +6,38 @@ ## ############################################################################## -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" -warn ( ) { +warn () { echo "$*" } -die ( ) { +die () { echo echo "$*" echo @@ -30,6 +48,7 @@ die ( ) { cygwin=false msys=false darwin=false +nonstop=false case "`uname`" in CYGWIN* ) cygwin=true @@ -40,26 +59,11 @@ case "`uname`" in MINGW* ) msys=true ;; + NONSTOP* ) + nonstop=true + ;; esac -# Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -85,7 +89,7 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then @@ -150,11 +154,19 @@ if $cygwin ; then esac fi -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " } -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index aec9973..e95643d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -8,14 +8,14 @@ @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome @@ -46,10 +46,9 @@ echo location of your Java installation. goto fail :init -@rem Get command-line arguments, handling Windowz variants +@rem Get command-line arguments, handling Windows variants if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. @@ -60,11 +59,6 @@ set _SKIP=2 if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ :execute @rem Setup the command line