Showing preview only (267K chars total). Download the full file or copy to clipboard to get everything.
Repository: square/sqlbrite
Branch: trunk
Commit: d4cf6ef96745
Files: 70
Total size: 245.7 KB
Directory structure:
gitextract_8j63fi6p/
├── .buildscript/
│ └── deploy_snapshot.sh
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── RELEASING.md
├── build.gradle
├── gradle/
│ ├── gradle-mvn-push.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── sample/
│ ├── build.gradle
│ ├── debug.keystore
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── sqlbrite/
│ │ └── todo/
│ │ ├── TodoApp.java
│ │ ├── TodoComponent.java
│ │ ├── TodoModule.java
│ │ ├── db/
│ │ │ ├── Db.java
│ │ │ ├── DbCallback.java
│ │ │ ├── DbModule.java
│ │ │ ├── TodoItem.java
│ │ │ └── TodoList.java
│ │ └── ui/
│ │ ├── ItemsAdapter.java
│ │ ├── ItemsFragment.java
│ │ ├── ListsAdapter.java
│ │ ├── ListsFragment.java
│ │ ├── ListsItem.java
│ │ ├── MainActivity.java
│ │ ├── NewItemFragment.java
│ │ └── NewListFragment.java
│ └── res/
│ ├── anim/
│ │ ├── slide_in_left.xml
│ │ ├── slide_in_right.xml
│ │ ├── slide_out_left.xml
│ │ └── slide_out_right.xml
│ ├── layout/
│ │ ├── items.xml
│ │ ├── lists.xml
│ │ ├── new_item.xml
│ │ └── new_list.xml
│ └── values/
│ └── strings.xml
├── settings.gradle
├── sqlbrite/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── squareup/
│ │ └── sqlbrite3/
│ │ ├── BlockingRecordingObserver.java
│ │ ├── BriteContentResolverTest.java
│ │ ├── BriteDatabaseTest.java
│ │ ├── QueryObservableTest.java
│ │ ├── QueryTest.java
│ │ ├── RecordingObserver.java
│ │ ├── SqlBriteTest.java
│ │ ├── TestDb.java
│ │ └── TestScheduler.java
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── squareup/
│ └── sqlbrite3/
│ ├── BriteContentResolver.java
│ ├── BriteDatabase.java
│ ├── QueryObservable.java
│ ├── QueryToListOperator.java
│ ├── QueryToOneOperator.java
│ ├── QueryToOptionalOperator.java
│ └── SqlBrite.java
├── sqlbrite-kotlin/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── squareup/
│ └── sqlbrite3/
│ └── extensions.kt
└── sqlbrite-lint/
├── build.gradle
└── src/
├── main/
│ └── java/
│ └── com/
│ └── squareup/
│ └── sqlbrite3/
│ ├── BriteIssueRegistry.kt
│ └── SqlBriteArgCountDetector.kt
└── test/
└── java/
└── com/
└── squareup/
└── sqlbrite3/
└── SqlBriteArgCountDetectorTest.kt
================================================
FILE CONTENTS
================================================
================================================
FILE: .buildscript/deploy_snapshot.sh
================================================
#!/bin/bash
#
# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.
#
# Adapted from https://coderwall.com/p/9b_lfq and
# http://benlimmer.com/2013/12/26/automatically-publish-javadoc-to-gh-pages-with-travis-ci/
SLUG="square/sqlbrite"
JDK="oraclejdk8"
BRANCH="master"
set -e
if [ "$TRAVIS_REPO_SLUG" != "$SLUG" ]; then
echo "Skipping snapshot deployment: wrong repository. Expected '$SLUG' but was '$TRAVIS_REPO_SLUG'."
elif [ "$TRAVIS_JDK_VERSION" != "$JDK" ]; then
echo "Skipping snapshot deployment: wrong JDK. Expected '$JDK' but was '$TRAVIS_JDK_VERSION'."
elif [ "$TRAVIS_PULL_REQUEST" != "false" ]; then
echo "Skipping snapshot deployment: was pull request."
elif [ "$TRAVIS_BRANCH" != "$BRANCH" ]; then
echo "Skipping snapshot deployment: wrong branch. Expected '$BRANCH' but was '$TRAVIS_BRANCH'."
else
echo "Deploying snapshot..."
./gradlew clean uploadArchives
echo "Snapshot deployed!"
fi
================================================
FILE: .gitignore
================================================
# IntelliJ IDEA
.idea
*.iml
# Gradle
.gradle
gradlew.bat
build
local.properties
reports
# Apple
.DS_Store
================================================
FILE: .travis.yml
================================================
language: android
android:
components:
- tools
- platform-tools
jdk:
- oraclejdk8
before_install:
# Install SDK license so Android Gradle plugin can install deps.
- mkdir "$ANDROID_HOME/licenses" || true
- echo "d56f5187479451eabf01fb78af6dfcb131a6481e" > "$ANDROID_HOME/licenses/android-sdk-license"
# Install the rest of tools (e.g., avdmanager)
- sdkmanager tools
# Install the system image
- sdkmanager "system-images;android-18;default;armeabi-v7a"
# Create and start emulator for the script. Meant to race the install task.
- echo no | avdmanager create avd --force -n test -k "system-images;android-18;default;armeabi-v7a"
- $ANDROID_HOME/emulator/emulator -avd test -no-audio -no-window &
install: ./gradlew clean assemble assembleAndroidTest --stacktrace
before_script:
- android-wait-for-emulator
- adb shell input keyevent 82
script: ./gradlew check connectedCheck --stacktrace
after_success:
- .buildscript/deploy_snapshot.sh
env:
global:
- secure: "NIWC0zkThskXn7uduTJ1yT78voqEgzEfw8tOImGNBjZ/NDU6yxM4bh+tq+fnkn5ENjELV6fgcYd2DUJSWmkFD2k9ZMRNLm//AqlQihl8aT+DpWhDdCkQjnolHnjm1O7+ys7Q/vswBZEzkBxzIgivajZEzvjarQItJjbpBftQ0Cs="
- secure: "ahPT9EzJVpkM4q2HA/VBxUzgicvfdOOZaEvOiQKJofy1FrLjrBS2LFxqCbyffg0sjGUyvBMLg767CSt/0xRRFWIpsjxCfmvEmAURi89zdZ8MUNXIwe7x/0lXCdQIt8eueq3Qh5qFwJUy4aFbzVvcmMXKswWzw1O0+IcvYX00/xc="
branches:
except:
- gh-pages
notifications:
email: false
sudo: false
cache:
directories:
- $HOME/.gradle
================================================
FILE: CHANGELOG.md
================================================
Change Log
==========
Version 3.2.0 *(2018-03-05)*
----------------------------
* New: Add `query(SupportSQLiteQuery)` method for one-off queries.
Version 3.1.1 *(2018-02-12)*
----------------------------
* Fix: Useless `BuildConfig` classes are no longer included.
* Fix: Eliminate Java interop checks for Kotlin extensions as they're only for Kotlin consumers and the checks exist in the Java code they delegate to anyway.
Version 3.1.0 *(2017-12-18)*
----------------------------
* New: `inTransaction` Kotlin extension function which handles starting, marking successful, and ending
a transaction.
* New: Embedded lint check which validates the number of arguments passed to `query` and `createQuery`
match the number of expected arguments of the SQL statement.
* Fix: Properly indent multi-line SQL statements in the logs for `query`.
Version 3.0.0 *(2017-11-28)*
----------------------------
Group ID has changed to `com.squareup.sqlbrite3`.
* New: Build on top of the Android architecture components Sqlite support library. This allows swapping
out the underlying Sqlite implementation to that of your choosing.
Because of the way the Sqlite support library works, there is no interop bridge between 1.x or 2.x to
this new version. If you haven't fully migrated to 2.x, complete that migration first and then upgrade
to 3.x all at once.
Version 2.0.0 *(2017-07-07)*
----------------------------
Group ID has changed to `com.squareup.sqlbrite2`.
* New: RxJava 2.x support. Backpressure is no longer supported as evidenced by the use of
`Observable`. If you want to slow down query notifications based on backpressure or another metric
like time then you should apply those operators yourself.
* New: `mapToOptional` for queries that return 0 or 1 rows.
* New: `sqlbrite-kotlin` module provides `mapTo*` extension functions for `Observable<Query>`.
* New: `sqlbrite-interop` module allows bridging 1.x and 2.x libraries together so that notifications
from each trigger queries from the other.
Note: This version only supports RxJava 2.
Version 1.1.2 *(2017-06-30)*
----------------------------
* Internal architecture changes to support the upcoming 2.0 release and a bridge allowing both 1.x
and 2.x to be used at the same time.
Version 1.1.1 *(2016-12-20)*
----------------------------
* Fix: Correct spelling of `getWritableDatabase()` to match `SQLiteOpenHelper`.
Version 1.1.0 *(2016-12-16)*
----------------------------
* New: Expose `getReadableDatabase()` and `getWriteableDatabase()` convenience methods.
* Fix: Do not cache instances of the readable and writable database internally as the framework
does this by default.
Version 1.0.0 *(2016-12-02)*
----------------------------
* RxJava dependency updated to 1.2.3.
* Restore `@WorkerThread` annotations to methods which do I/O. If you're using Java 8 with
Retrolambda or Jack you need to use version 2.3 or newer of the Android Gradle plugin to have
these annotations correctly handled by lint.
Version 0.8.0 *(2016-10-21)*
----------------------------
* New: A `Transformer<Query, Query>` can be supplied which is applied to each returned observable.
* New: `newNonExclusiveTransaction()` starts transactions in `IMMEDIATE` mode. See the platform
or SQLite documentation for more information.
* New: APIs for insert/update/delete which allow providing a compiled `SQLiteStatement`.
Version 0.7.0 *(2016-07-06)*
----------------------------
* New: Allow `mapTo*` mappers to return `null` values. This is useful when querying on a single,
nullable column for which `null` is a valid value.
* Fix: When `mapToOne` does not emit a value downstream, request another value from upstream to
ensure fixed-item requests (such as `take(1)`) as properly honored.
* Fix: Add logging to synchronous `execute` methods.
Version 0.6.3 *(2016-04-13)*
----------------------------
* `QueryObservable` constructor is now public allow instances to be created for tests.
Version 0.6.2 *(2016-03-01)*
----------------------------
* Fix: Document explicitly and correctly handle the fact that `Query.run()` can return `null` in
some situations. The `mapToOne`, `mapToOneOrDefault`, `mapToList`, and `asRows` helpers have all
been updated to handle this case and each is documented with their respective behavior.
Version 0.6.1 *(2016-02-29)*
----------------------------
* Fix: Apply backpressure strategy between database/content provider and the supplied `Scheduler`.
This guards against backpressure exceptions when the scheduler is unable to keep up with the rate
at which queries are being triggered.
* Fix: Indent the subsequent lines of a multi-line queries when logging.
Version 0.6.0 *(2016-02-17)*
----------------------------
* New: Require a `Scheduler` when wrapping a database or content provider which will be used when
sending query triggers. This allows the query to be run in subsequent operators without needing an
additional `observeOn`. It also eliminates the need to use `subscribeOn` since the supplied
`Scheduler` will be used for all emissions (similar to RxJava's `timer`, `interval`, etc.).
This also corrects a potential violation of the RxJava contract and potential source of bugs in that
all triggers will occur on the supplied `Scheduler`. Previously the initial value would trigger
synchronously (on the subscribing thread) while subsequent ones trigger on the thread which
performed the transaction. The new behavior puts the initial trigger on the same thread as all
subsequent triggers and also does not force transactions to block while sending triggers.
Version 0.5.1 *(2016-02-03)*
----------------------------
* New: Query logs now contain timing information on how long they took to execute. This only covers
the time until a `Cursor` was made available, not object mapping or delivering to subscribers.
* Fix: Switch query logging to happen when `Query.run` is called, not when a query is triggered.
* Fix: Check for subscribing inside a transaction using a more accurate primitive.
Version 0.5.0 *(2015-12-09)*
----------------------------
* New: Expose `mapToOne`, `mapToOneOrDefault`, and `mapToList` as static methods on `Query`. These
mirror the behavior of the methods of the same name on `QueryObservable` but can be used later in
a stream by passing the returned `Operator` instances to `lift()` (e.g.,
`take(1).lift(Query.mapToOne(..))`).
* Requires RxJava 1.1.0 or newer.
Version 0.4.1 *(2015-10-19)*
----------------------------
* New: `execute` method provides the ability to execute arbitrary SQL statements.
* New: `executeAndTrigger` method provides the ability to execute arbitrary SQL statements and
notifying any queries to update on the specified table.
* Fix: `Query.asRows` no longer calls `onCompleted` when the downstream subscriber has unsubscribed.
Version 0.4.0 *(2015-09-22)*
----------------------------
* New: `mapToOneOrDefault` replaces `mapToOneOrNull` for more flexibility.
* Fix: Notifications of table updates as the result of a transaction now occur after the transaction
has been applied. Previous the notification would happen during the commit at which time it was
invalid to create a new transaction in a subscriber.
Version 0.3.1 *(2015-09-02)*
----------------------------
* New: `mapToOne` and `mapToOneOrNull` operators on `QueryObservable`. These work on queries which
return 0 or 1 rows and are a convenience for turning them into a type `T` given a mapper of type
`Func1<Cursor, T>` (the same which can be used for `mapToList`).
* Fix: Remove `@WorkerThread` annotations for now. Various combinations of lint, RxJava, and
retrolambda can cause false-positives.
Version 0.3.0 *(2015-08-31)*
----------------------------
* Transactions are now exposed as objects instead of methods. Call `newTransaction()` to start a
transaction. On the `Transaction` instance, call `markSuccessful()` to indicate success and
`end()` to commit or rollback the transaction. The `Transaction` instance implements `Closeable`
to allow its use in a try-with-resources construct. See the `newTransaction()` Javadoc for more
information.
* `Query` instances can now be turned directly into an `Observable<T>` by calling `asRows` with a
`Func1<Cursor, T>` that maps rows to a type `T`. This allows easy filtering and limiting in
memory rather than in the query. See the `asRows` Javadoc for more information.
* `createQuery` now returns a `QueryObservable` which offers a `mapToList` operator. This operator
also takes a `Func1<Cursor, T>` for mapping rows to a type `T`, but instead of individual rows it
collects all the rows into a list. For large query results or frequently updated tables this can
create a lot of objects. See the `mapToList` Javadoc for more information.
* New: Nullability, `@CheckResult`, and `@WorkerThread` annotations on all APIs allow a more useful
interaction with lint in consuming projects.
Version 0.2.1 *(2015-07-14)*
----------------------------
* Fix: Add support for backpressure.
Version 0.2.0 *(2015-06-30)*
----------------------------
* An `Observable<Query>` can now be created from wrapping a `ContentResolver` in order to observe
queries from another app's content provider.
* `SqlBrite` class is now a factory for both a `BriteDatabase` (the `SQLiteOpenHelper` wrapper)
and `BriteContentResolver` (the `ContentResolver` wrapper).
Version 0.1.0 *(2015-02-21)*
----------------------------
Initial release.
================================================
FILE: CONTRIBUTING.md
================================================
Contributing
============
If you would like to contribute code you can do so through GitHub by forking
the repository and sending a pull request.
When submitting code, please make every effort to follow existing conventions
and style in order to keep the code as readable as possible. Please also make
sure your code compiles by running `./gradlew clean build`.
Before your code can be accepted into the project you must also sign the
[Individual Contributor License Agreement (CLA)][1].
[1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
SQL Brite
=========
A lightweight wrapper around `SupportSQLiteOpenHelper` and `ContentResolver` which introduces reactive
stream semantics to queries.
# Deprecated
This library is no longer actively developed and is considered complete.
Its database features (and far, far more) are now offered by [SQLDelight](https://github.com/cashapp/sqldelight/)
and its [upgrading guide](https://github.com/cashapp/sqldelight/blob/1.0.0/UPGRADING.md) offers some
migration help.
For content provider monitoring please use [Copper](https://github.com/cashapp/copper) instead.
Usage
-----
Create a `SqlBrite` instance which is an adapter for the library functionality.
```java
SqlBrite sqlBrite = new SqlBrite.Builder().build();
```
Pass a `SupportSQLiteOpenHelper` instance and a `Scheduler` to create a `BriteDatabase`.
```java
BriteDatabase db = sqlBrite.wrapDatabaseHelper(openHelper, Schedulers.io());
```
A `Scheduler` is required for a few reasons, but the most important is that query notifications can
trigger on the thread of your choice. The query can then be run without blocking the main thread or
the thread which caused the trigger.
The `BriteDatabase.createQuery` method is similar to `SupportSQLiteDatabase.query` except it takes an
additional parameter of table(s) on which to listen for changes. Subscribe to the returned
`Observable<Query>` which will immediately notify with a `Query` to run.
```java
Observable<Query> users = db.createQuery("users", "SELECT * FROM users");
users.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
Cursor cursor = query.run();
// TODO parse data...
}
});
```
Unlike a traditional `query`, updates to the specified table(s) will trigger additional
notifications for as long as you remain subscribed to the observable. This means that when you
insert, update, or delete data, any subscribed queries will update with the new data instantly.
```java
final AtomicInteger queries = new AtomicInteger();
users.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
queries.getAndIncrement();
}
});
System.out.println("Queries: " + queries.get()); // Prints 1
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("jw", "Jake Wharton"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("mattp", "Matt Precious"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("strong", "Alec Strong"));
System.out.println("Queries: " + queries.get()); // Prints 4
```
In the previous example we re-used the `BriteDatabase` object "db" for inserts. All insert, update,
or delete operations must go through this object in order to correctly notify subscribers.
Unsubscribe from the returned `Subscription` to stop getting updates.
```java
final AtomicInteger queries = new AtomicInteger();
Subscription s = users.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
queries.getAndIncrement();
}
});
System.out.println("Queries: " + queries.get()); // Prints 1
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("jw", "Jake Wharton"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("mattp", "Matt Precious"));
s.unsubscribe();
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("strong", "Alec Strong"));
System.out.println("Queries: " + queries.get()); // Prints 3
```
Use transactions to prevent large changes to the data from spamming your subscribers.
```java
final AtomicInteger queries = new AtomicInteger();
users.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
queries.getAndIncrement();
}
});
System.out.println("Queries: " + queries.get()); // Prints 1
Transaction transaction = db.newTransaction();
try {
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("jw", "Jake Wharton"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("mattp", "Matt Precious"));
db.insert("users", SQLiteDatabase.CONFLICT_ABORT, createUser("strong", "Alec Strong"));
transaction.markSuccessful();
} finally {
transaction.end();
}
System.out.println("Queries: " + queries.get()); // Prints 2
```
*Note: You can also use try-with-resources with a `Transaction` instance.*
Since queries are just regular RxJava `Observable` objects, operators can also be used to
control the frequency of notifications to subscribers.
```java
users.debounce(500, MILLISECONDS).subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
// TODO...
}
});
```
The `SqlBrite` object can also wrap a `ContentResolver` for observing a query on another app's
content provider.
```java
BriteContentResolver resolver = sqlBrite.wrapContentProvider(contentResolver, Schedulers.io());
Observable<Query> query = resolver.createQuery(/*...*/);
```
The full power of RxJava's operators are available for combining, filtering, and triggering any
number of queries and data changes.
Philosophy
----------
SQL Brite's only responsibility is to be a mechanism for coordinating and composing the notification
of updates to tables such that you can update queries as soon as data changes.
This library is not an ORM. It is not a type-safe query mechanism. It won't serialize the same POJOs
you use for Gson. It's not going to perform database migrations for you.
Some of these features are offered by [SQL Delight][sqldelight] which can be used with SQL Brite.
Download
--------
```groovy
implementation 'com.squareup.sqlbrite3:sqlbrite:3.2.0'
```
For the 'kotlin' module that adds extension functions to `Observable<Query>`:
```groovy
implementation 'com.squareup.sqlbrite3:sqlbrite-kotlin:3.2.0'
```
Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap].
License
-------
Copyright 2015 Square, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
[snap]: https://oss.sonatype.org/content/repositories/snapshots/
[sqldelight]: https://github.com/square/sqldelight/
================================================
FILE: RELEASING.md
================================================
Releasing
========
1. Change the version in `gradle.properties` to a non-SNAPSHOT version.
2. Update the `CHANGELOG.md` for the impending release.
3. Update the `README.md` with the new version.
4. `git commit -am "Prepare for release X.Y.Z."` (where X.Y.Z is the new version)
5. `./gradlew clean uploadArchives`.
6. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact.
7. `git tag -a X.Y.Z -m "Version X.Y.Z"` (where X.Y.Z is the new version)
8. Update the `gradle.properties` to the next SNAPSHOT version.
9. `git commit -am "Prepare next development version."`
10. `git push && git push --tags`
If step 5 or 6 fails, drop the Sonatype repo, fix the problem, commit, and start again at step 5.
================================================
FILE: build.gradle
================================================
buildscript {
ext.versions = [
'minSdk': 14,
'compileSdk': 27,
'kotlin': '1.1.60',
'lint': '26.0.1'
]
repositories {
mavenCentral()
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
}
allprojects {
repositories {
mavenCentral()
google()
jcenter()
}
group = GROUP
version = VERSION_NAME
}
ext {
// Android dependencies.
supportV4 = 'com.android.support:support-v4:27.0.0'
supportAnnotations = 'com.android.support:support-annotations:27.0.0'
supportTestRunner = 'com.android.support.test:runner:0.5'
supportSqlite = 'android.arch.persistence:db:1.0.0'
supportSqliteFramework = 'android.arch.persistence:db-framework:1.0.0'
// Third-party dependencies.
kotlinStdLib = "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
dagger = 'com.google.dagger:dagger:2.13'
daggerCompiler = 'com.google.dagger:dagger-compiler:2.13'
butterKnifeRuntime = 'com.jakewharton:butterknife:8.8.1'
butterKnifeCompiler = 'com.jakewharton:butterknife-compiler:8.8.1'
timber = 'com.jakewharton.timber:timber:4.6.0'
autoValue = 'com.google.auto.value:auto-value:1.5'
autoValueParcel = 'com.ryanharter.auto.value:auto-value-parcel:0.2.5'
rxJava = 'io.reactivex.rxjava2:rxjava:2.1.3'
rxAndroid = 'io.reactivex.rxjava2:rxandroid:2.0.1'
rxBinding = 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
junit = 'junit:junit:4.12'
truth = 'com.google.truth:truth:0.36'
// Lint dependencies.
lintApi = "com.android.tools.lint:lint-api:${versions.lint}"
lint = "com.android.tools.lint:lint:${versions.lint}"
lintTests = "com.android.tools.lint:lint-tests:${versions.lint}"
}
configurations {
osstrich
}
dependencies {
osstrich 'com.squareup.osstrich:osstrich:1.2.0'
}
task publishV1Javadoc(type: JavaExec) {
classpath = configurations.osstrich
main = 'com.squareup.osstrich.JavadocPublisher'
args = [
'build/javadoc',
'https://github.com/square/sqlbrite',
'com.squareup.sqlbrite'
]
}
task publishV2Javadoc(type: JavaExec) {
classpath = configurations.osstrich
main = 'com.squareup.osstrich.JavadocPublisher'
args = [
'build/javadoc',
'https://github.com/square/sqlbrite',
'com.squareup.sqlbrite2'
]
}
task publishV3Javadoc(type: JavaExec) {
classpath = configurations.osstrich
main = 'com.squareup.osstrich.JavadocPublisher'
args = [
'build/javadoc',
'https://github.com/square/sqlbrite',
'com.squareup.sqlbrite3'
]
}
task publishJavadoc(dependsOn: [publishV1Javadoc, publishV2Javadoc, publishV3Javadoc])
================================================
FILE: gradle/gradle-mvn-push.gradle
================================================
/*
* Copyright 2013 Chris Banes
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
apply plugin: 'maven'
apply plugin: 'signing'
def isReleaseBuild() {
return VERSION_NAME.contains("SNAPSHOT") == false
}
def getRepositoryUsername() {
return hasProperty('SONATYPE_NEXUS_USERNAME') ? SONATYPE_NEXUS_USERNAME : ""
}
def getRepositoryPassword() {
return hasProperty('SONATYPE_NEXUS_PASSWORD') ? SONATYPE_NEXUS_PASSWORD : ""
}
afterEvaluate { project ->
uploadArchives {
repositories {
mavenDeployer {
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
pom.groupId = GROUP
pom.artifactId = POM_ARTIFACT_ID
pom.version = VERSION_NAME
repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
snapshotRepository(url: "https://oss.sonatype.org/content/repositories/snapshots/") {
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
}
pom.project {
name POM_NAME
packaging POM_PACKAGING
description POM_DESCRIPTION
url POM_URL
scm {
url POM_SCM_URL
connection POM_SCM_CONNECTION
developerConnection POM_SCM_DEV_CONNECTION
}
licenses {
license {
name POM_LICENCE_NAME
url POM_LICENCE_URL
distribution POM_LICENCE_DIST
}
}
developers {
developer {
id POM_DEVELOPER_ID
name POM_DEVELOPER_NAME
}
}
}
}
}
}
signing {
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
sign configurations.archives
}
task androidJavadocs(type: Javadoc) {
if (!project.plugins.hasPlugin('kotlin-android')) {
source = android.sourceSets.main.java.srcDirs
}
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
if (JavaVersion.current().isJava8Compatible()) {
options.addStringOption('Xdoclint:none', '-quiet')
}
}
task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) {
classifier = 'javadoc'
from androidJavadocs.destinationDir
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.sourceFiles
}
artifacts {
archives androidSourcesJar
archives androidJavadocsJar
}
}
================================================
FILE: gradle/wrapper/gradle-wrapper.properties
================================================
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
================================================
FILE: gradle.properties
================================================
GROUP=com.squareup.sqlbrite3
VERSION_NAME=3.2.1-SNAPSHOT
POM_DESCRIPTION=A lightweight wrapper around SQLiteOpenHelper which introduces reactive stream semantics to SQL operations.
POM_URL=http://github.com/square/sqlbrite/
POM_SCM_URL=http://github.com/square/sqlbrite/
POM_SCM_CONNECTION=scm:git:git://github.com/square/sqlbrite.git
POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/square/sqlbrite.git
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo
POM_DEVELOPER_ID=square
POM_DEVELOPER_NAME=Square, Inc.
================================================
FILE: gradlew
================================================
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# 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 () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
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
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
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" "$@"
================================================
FILE: sample/build.gradle
================================================
apply plugin: 'com.android.application'
dependencies {
implementation rootProject.ext.supportV4
implementation rootProject.ext.supportAnnotations
implementation rootProject.ext.dagger
annotationProcessor rootProject.ext.daggerCompiler
implementation rootProject.ext.butterKnifeRuntime
annotationProcessor rootProject.ext.butterKnifeCompiler
implementation rootProject.ext.timber
implementation rootProject.ext.rxJava
implementation rootProject.ext.rxAndroid
implementation rootProject.ext.rxBinding
compileOnly rootProject.ext.autoValue
annotationProcessor rootProject.ext.autoValue
annotationProcessor rootProject.ext.autoValueParcel
implementation project(':sqlbrite')
implementation rootProject.ext.supportSqliteFramework
}
android {
compileSdkVersion versions.compileSdk
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
lintOptions {
textOutput 'stdout'
textReport true
ignore 'InvalidPackage' // Provided AutoValue pulls in Guava and friends. Doesn't end up in APK.
}
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.compileSdk
applicationId 'com.example.sqlbrite.todo'
versionCode 1
versionName '1.0'
}
signingConfigs {
debug {
storeFile file('debug.keystore')
storePassword 'android'
keyAlias 'android'
keyPassword 'android'
}
}
buildTypes {
debug {
applicationIdSuffix '.development'
signingConfig signingConfigs.debug
}
}
}
================================================
FILE: sample/src/main/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sqlbrite.todo"
>
<application
android:label="@string/app_name"
android:name=".TodoApp"
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
>
<activity
android:name=".ui.MainActivity"
>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
</manifest>
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/TodoApp.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo;
import android.app.Application;
import android.content.Context;
import timber.log.Timber;
public final class TodoApp extends Application {
private TodoComponent mainComponent;
@Override public void onCreate() {
super.onCreate();
if (BuildConfig.DEBUG) {
Timber.plant(new Timber.DebugTree());
}
mainComponent = DaggerTodoComponent.builder().todoModule(new TodoModule(this)).build();
}
public static TodoComponent getComponent(Context context) {
return ((TodoApp) context.getApplicationContext()).mainComponent;
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/TodoComponent.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo;
import com.example.sqlbrite.todo.ui.ItemsFragment;
import com.example.sqlbrite.todo.ui.ListsFragment;
import com.example.sqlbrite.todo.ui.NewItemFragment;
import com.example.sqlbrite.todo.ui.NewListFragment;
import dagger.Component;
import javax.inject.Singleton;
@Singleton
@Component(modules = TodoModule.class)
public interface TodoComponent {
void inject(ListsFragment fragment);
void inject(ItemsFragment fragment);
void inject(NewItemFragment fragment);
void inject(NewListFragment fragment);
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/TodoModule.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo;
import android.app.Application;
import com.example.sqlbrite.todo.db.DbModule;
import dagger.Module;
import dagger.Provides;
import javax.inject.Singleton;
@Module(
includes = {
DbModule.class,
}
)
public final class TodoModule {
private final Application application;
TodoModule(Application application) {
this.application = application;
}
@Provides @Singleton Application provideApplication() {
return application;
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/Db.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.db;
import android.database.Cursor;
public final class Db {
public static final int BOOLEAN_FALSE = 0;
public static final int BOOLEAN_TRUE = 1;
public static String getString(Cursor cursor, String columnName) {
return cursor.getString(cursor.getColumnIndexOrThrow(columnName));
}
public static boolean getBoolean(Cursor cursor, String columnName) {
return getInt(cursor, columnName) == BOOLEAN_TRUE;
}
public static long getLong(Cursor cursor, String columnName) {
return cursor.getLong(cursor.getColumnIndexOrThrow(columnName));
}
public static int getInt(Cursor cursor, String columnName) {
return cursor.getInt(cursor.getColumnIndexOrThrow(columnName));
}
private Db() {
throw new AssertionError("No instances.");
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/DbCallback.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.db;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.db.SupportSQLiteOpenHelper;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL;
final class DbCallback extends SupportSQLiteOpenHelper.Callback {
private static final int VERSION = 1;
private static final String CREATE_LIST = ""
+ "CREATE TABLE " + TodoList.TABLE + "("
+ TodoList.ID + " INTEGER NOT NULL PRIMARY KEY,"
+ TodoList.NAME + " TEXT NOT NULL,"
+ TodoList.ARCHIVED + " INTEGER NOT NULL DEFAULT 0"
+ ")";
private static final String CREATE_ITEM = ""
+ "CREATE TABLE " + TodoItem.TABLE + "("
+ TodoItem.ID + " INTEGER NOT NULL PRIMARY KEY,"
+ TodoItem.LIST_ID + " INTEGER NOT NULL REFERENCES " + TodoList.TABLE + "(" + TodoList.ID + "),"
+ TodoItem.DESCRIPTION + " TEXT NOT NULL,"
+ TodoItem.COMPLETE + " INTEGER NOT NULL DEFAULT 0"
+ ")";
private static final String CREATE_ITEM_LIST_ID_INDEX =
"CREATE INDEX item_list_id ON " + TodoItem.TABLE + " (" + TodoItem.LIST_ID + ")";
DbCallback() {
super(VERSION);
}
@Override public void onCreate(SupportSQLiteDatabase db) {
db.execSQL(CREATE_LIST);
db.execSQL(CREATE_ITEM);
db.execSQL(CREATE_ITEM_LIST_ID_INDEX);
long groceryListId = db.insert(TodoList.TABLE, CONFLICT_FAIL, new TodoList.Builder()
.name("Grocery List")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(groceryListId)
.description("Beer")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(groceryListId)
.description("Point Break on DVD")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(groceryListId)
.description("Bad Boys 2 on DVD")
.build());
long holidayPresentsListId = db.insert(TodoList.TABLE, CONFLICT_FAIL, new TodoList.Builder()
.name("Holiday Presents")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(holidayPresentsListId)
.description("Pogo Stick for Jake W.")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(holidayPresentsListId)
.description("Jack-in-the-box for Alec S.")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(holidayPresentsListId)
.description("Pogs for Matt P.")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(holidayPresentsListId)
.description("Cola for Jesse W.")
.build());
long workListId = db.insert(TodoList.TABLE, CONFLICT_FAIL, new TodoList.Builder()
.name("Work Items")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(workListId)
.description("Finish SqlBrite library")
.complete(true)
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(workListId)
.description("Finish SqlBrite sample app")
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder()
.listId(workListId)
.description("Publish SqlBrite to GitHub")
.build());
long birthdayPresentsListId = db.insert(TodoList.TABLE, CONFLICT_FAIL, new TodoList.Builder()
.name("Birthday Presents")
.archived(true)
.build());
db.insert(TodoItem.TABLE, CONFLICT_FAIL, new TodoItem.Builder().listId(birthdayPresentsListId)
.description("New car")
.complete(true)
.build());
}
@Override public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/DbModule.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.db;
import android.app.Application;
import android.arch.persistence.db.SupportSQLiteOpenHelper;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Factory;
import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
import com.squareup.sqlbrite3.BriteDatabase;
import com.squareup.sqlbrite3.SqlBrite;
import dagger.Module;
import dagger.Provides;
import io.reactivex.schedulers.Schedulers;
import javax.inject.Singleton;
import timber.log.Timber;
@Module
public final class DbModule {
@Provides @Singleton SqlBrite provideSqlBrite() {
return new SqlBrite.Builder()
.logger(new SqlBrite.Logger() {
@Override public void log(String message) {
Timber.tag("Database").v(message);
}
})
.build();
}
@Provides @Singleton BriteDatabase provideDatabase(SqlBrite sqlBrite, Application application) {
Configuration configuration = Configuration.builder(application)
.name("todo.db")
.callback(new DbCallback())
.build();
Factory factory = new FrameworkSQLiteOpenHelperFactory();
SupportSQLiteOpenHelper helper = factory.create(configuration);
BriteDatabase db = sqlBrite.wrapDatabaseHelper(helper, Schedulers.io());
db.setLoggingEnabled(true);
return db;
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/TodoItem.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.db;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Parcelable;
import com.google.auto.value.AutoValue;
import io.reactivex.functions.Function;
@AutoValue
public abstract class TodoItem implements Parcelable {
public static final String TABLE = "todo_item";
public static final String ID = "_id";
public static final String LIST_ID = "todo_list_id";
public static final String DESCRIPTION = "description";
public static final String COMPLETE = "complete";
public abstract long id();
public abstract long listId();
public abstract String description();
public abstract boolean complete();
public static final Function<Cursor, TodoItem> MAPPER = new Function<Cursor, TodoItem>() {
@Override public TodoItem apply(Cursor cursor) {
long id = Db.getLong(cursor, ID);
long listId = Db.getLong(cursor, LIST_ID);
String description = Db.getString(cursor, DESCRIPTION);
boolean complete = Db.getBoolean(cursor, COMPLETE);
return new AutoValue_TodoItem(id, listId, description, complete);
}
};
public static final class Builder {
private final ContentValues values = new ContentValues();
public Builder id(long id) {
values.put(ID, id);
return this;
}
public Builder listId(long listId) {
values.put(LIST_ID, listId);
return this;
}
public Builder description(String description) {
values.put(DESCRIPTION, description);
return this;
}
public Builder complete(boolean complete) {
values.put(COMPLETE, complete ? Db.BOOLEAN_TRUE : Db.BOOLEAN_FALSE);
return this;
}
public ContentValues build() {
return values; // TODO defensive copy?
}
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/TodoList.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.db;
import android.content.ContentValues;
import android.os.Parcelable;
import com.google.auto.value.AutoValue;
// Note: normally I wouldn't prefix table classes but I didn't want 'List' to be overloaded.
@AutoValue
public abstract class TodoList implements Parcelable {
public static final String TABLE = "todo_list";
public static final String ID = "_id";
public static final String NAME = "name";
public static final String ARCHIVED = "archived";
public abstract long id();
public abstract String name();
public abstract boolean archived();
public static final class Builder {
private final ContentValues values = new ContentValues();
public Builder id(long id) {
values.put(ID, id);
return this;
}
public Builder name(String name) {
values.put(NAME, name);
return this;
}
public Builder archived(boolean archived) {
values.put(ARCHIVED, archived);
return this;
}
public ContentValues build() {
return values; // TODO defensive copy?
}
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ItemsAdapter.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.ui;
import android.content.Context;
import android.text.SpannableString;
import android.text.style.StrikethroughSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckedTextView;
import com.example.sqlbrite.todo.db.TodoItem;
import io.reactivex.functions.Consumer;
import java.util.Collections;
import java.util.List;
final class ItemsAdapter extends BaseAdapter implements Consumer<List<TodoItem>> {
private final LayoutInflater inflater;
private List<TodoItem> items = Collections.emptyList();
public ItemsAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
@Override public void accept(List<TodoItem> items) {
this.items = items;
notifyDataSetChanged();
}
@Override public int getCount() {
return items.size();
}
@Override public TodoItem getItem(int position) {
return items.get(position);
}
@Override public long getItemId(int position) {
return getItem(position).id();
}
@Override public boolean hasStableIds() {
return true;
}
@Override public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(android.R.layout.simple_list_item_multiple_choice, parent, false);
}
TodoItem item = getItem(position);
CheckedTextView textView = (CheckedTextView) convertView;
textView.setChecked(item.complete());
CharSequence description = item.description();
if (item.complete()) {
SpannableString spannable = new SpannableString(description);
spannable.setSpan(new StrikethroughSpan(), 0, description.length(), 0);
description = spannable;
}
textView.setText(description);
return convertView;
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ItemsFragment.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.ui;
import android.app.Activity;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.example.sqlbrite.todo.R;
import com.example.sqlbrite.todo.TodoApp;
import com.example.sqlbrite.todo.db.Db;
import com.example.sqlbrite.todo.db.TodoItem;
import com.example.sqlbrite.todo.db.TodoList;
import com.jakewharton.rxbinding2.widget.AdapterViewItemClickEvent;
import com.jakewharton.rxbinding2.widget.RxAdapterView;
import com.squareup.sqlbrite3.BriteDatabase;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import javax.inject.Inject;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_NONE;
import static android.support.v4.view.MenuItemCompat.SHOW_AS_ACTION_IF_ROOM;
import static android.support.v4.view.MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT;
import static com.squareup.sqlbrite3.SqlBrite.Query;
public final class ItemsFragment extends Fragment {
private static final String KEY_LIST_ID = "list_id";
private static final String LIST_QUERY = "SELECT * FROM "
+ TodoItem.TABLE
+ " WHERE "
+ TodoItem.LIST_ID
+ " = ? ORDER BY "
+ TodoItem.COMPLETE
+ " ASC";
private static final String COUNT_QUERY = "SELECT COUNT(*) FROM "
+ TodoItem.TABLE
+ " WHERE "
+ TodoItem.COMPLETE
+ " = "
+ Db.BOOLEAN_FALSE
+ " AND "
+ TodoItem.LIST_ID
+ " = ?";
private static final String TITLE_QUERY =
"SELECT " + TodoList.NAME + " FROM " + TodoList.TABLE + " WHERE " + TodoList.ID + " = ?";
public interface Listener {
void onNewItemClicked(long listId);
}
public static ItemsFragment newInstance(long listId) {
Bundle arguments = new Bundle();
arguments.putLong(KEY_LIST_ID, listId);
ItemsFragment fragment = new ItemsFragment();
fragment.setArguments(arguments);
return fragment;
}
@Inject BriteDatabase db;
@BindView(android.R.id.list) ListView listView;
@BindView(android.R.id.empty) View emptyView;
private Listener listener;
private ItemsAdapter adapter;
private CompositeDisposable disposables;
private long getListId() {
return getArguments().getLong(KEY_LIST_ID);
}
@Override public void onAttach(Activity activity) {
if (!(activity instanceof Listener)) {
throw new IllegalStateException("Activity must implement fragment Listener.");
}
super.onAttach(activity);
TodoApp.getComponent(activity).inject(this);
setHasOptionsMenu(true);
listener = (Listener) activity;
adapter = new ItemsAdapter(activity);
}
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
MenuItem item = menu.add(R.string.new_item)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
listener.onNewItemClicked(getListId());
return true;
}
});
MenuItemCompat.setShowAsAction(item, SHOW_AS_ACTION_IF_ROOM | SHOW_AS_ACTION_WITH_TEXT);
}
@Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.items, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
listView.setEmptyView(emptyView);
listView.setAdapter(adapter);
RxAdapterView.itemClickEvents(listView) //
.observeOn(Schedulers.io())
.subscribe(new Consumer<AdapterViewItemClickEvent>() {
@Override public void accept(AdapterViewItemClickEvent event) {
boolean newValue = !adapter.getItem(event.position()).complete();
db.update(TodoItem.TABLE, CONFLICT_NONE,
new TodoItem.Builder().complete(newValue).build(), TodoItem.ID + " = ?",
String.valueOf(event.id()));
}
});
}
@Override public void onResume() {
super.onResume();
String listId = String.valueOf(getListId());
disposables = new CompositeDisposable();
Observable<Integer> itemCount = db.createQuery(TodoItem.TABLE, COUNT_QUERY, listId) //
.map(new Function<Query, Integer>() {
@Override public Integer apply(Query query) {
Cursor cursor = query.run();
try {
if (!cursor.moveToNext()) {
throw new AssertionError("No rows");
}
return cursor.getInt(0);
} finally {
cursor.close();
}
}
});
Observable<String> listName =
db.createQuery(TodoList.TABLE, TITLE_QUERY, listId).map(new Function<Query, String>() {
@Override public String apply(Query query) {
Cursor cursor = query.run();
try {
if (!cursor.moveToNext()) {
throw new AssertionError("No rows");
}
return cursor.getString(0);
} finally {
cursor.close();
}
}
});
disposables.add(
Observable.combineLatest(listName, itemCount, new BiFunction<String, Integer, String>() {
@Override public String apply(String listName, Integer itemCount) {
return listName + " (" + itemCount + ")";
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override public void accept(String title) throws Exception {
getActivity().setTitle(title);
}
}));
disposables.add(db.createQuery(TodoItem.TABLE, LIST_QUERY, listId)
.mapToList(TodoItem.MAPPER)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(adapter));
}
@Override public void onPause() {
super.onPause();
disposables.dispose();
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ListsAdapter.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.ui;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import io.reactivex.functions.Consumer;
import java.util.Collections;
import java.util.List;
final class ListsAdapter extends BaseAdapter implements Consumer<List<ListsItem>> {
private final LayoutInflater inflater;
private List<ListsItem> items = Collections.emptyList();
public ListsAdapter(Context context) {
this.inflater = LayoutInflater.from(context);
}
@Override public void accept(List<ListsItem> items) {
this.items = items;
notifyDataSetChanged();
}
@Override public int getCount() {
return items.size();
}
@Override public ListsItem getItem(int position) {
return items.get(position);
}
@Override public long getItemId(int position) {
return getItem(position).id();
}
@Override public boolean hasStableIds() {
return true;
}
@Override public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(android.R.layout.simple_list_item_1, parent, false);
}
ListsItem item = getItem(position);
((TextView) convertView).setText(item.name() + " (" + item.itemCount() + ")");
return convertView;
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ListsFragment.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.ui;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnItemClick;
import com.example.sqlbrite.todo.R;
import com.example.sqlbrite.todo.TodoApp;
import com.squareup.sqlbrite3.BriteDatabase;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import javax.inject.Inject;
import static android.support.v4.view.MenuItemCompat.SHOW_AS_ACTION_IF_ROOM;
import static android.support.v4.view.MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT;
public final class ListsFragment extends Fragment {
interface Listener {
void onListClicked(long id);
void onNewListClicked();
}
static ListsFragment newInstance() {
return new ListsFragment();
}
@Inject BriteDatabase db;
@BindView(android.R.id.list) ListView listView;
@BindView(android.R.id.empty) View emptyView;
private Listener listener;
private ListsAdapter adapter;
private Disposable disposable;
@Override public void onAttach(Activity activity) {
if (!(activity instanceof Listener)) {
throw new IllegalStateException("Activity must implement fragment Listener.");
}
super.onAttach(activity);
TodoApp.getComponent(activity).inject(this);
setHasOptionsMenu(true);
listener = (Listener) activity;
adapter = new ListsAdapter(activity);
}
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
MenuItem item = menu.add(R.string.new_list)
.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override public boolean onMenuItemClick(MenuItem item) {
listener.onNewListClicked();
return true;
}
});
MenuItemCompat.setShowAsAction(item, SHOW_AS_ACTION_IF_ROOM | SHOW_AS_ACTION_WITH_TEXT);
}
@Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.lists, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
listView.setEmptyView(emptyView);
listView.setAdapter(adapter);
}
@OnItemClick(android.R.id.list) void listClicked(long listId) {
listener.onListClicked(listId);
}
@Override public void onResume() {
super.onResume();
getActivity().setTitle("To-Do");
disposable = db.createQuery(ListsItem.TABLES, ListsItem.QUERY)
.mapToList(ListsItem.MAPPER)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(adapter);
}
@Override public void onPause() {
super.onPause();
disposable.dispose();
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ListsItem.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.ui;
import android.database.Cursor;
import android.os.Parcelable;
import com.example.sqlbrite.todo.db.Db;
import com.example.sqlbrite.todo.db.TodoItem;
import com.example.sqlbrite.todo.db.TodoList;
import com.google.auto.value.AutoValue;
import io.reactivex.functions.Function;
import java.util.Arrays;
import java.util.Collection;
@AutoValue
abstract class ListsItem implements Parcelable {
private static String ALIAS_LIST = "list";
private static String ALIAS_ITEM = "item";
private static String LIST_ID = ALIAS_LIST + "." + TodoList.ID;
private static String LIST_NAME = ALIAS_LIST + "." + TodoList.NAME;
private static String ITEM_COUNT = "item_count";
private static String ITEM_ID = ALIAS_ITEM + "." + TodoItem.ID;
private static String ITEM_LIST_ID = ALIAS_ITEM + "." + TodoItem.LIST_ID;
public static Collection<String> TABLES = Arrays.asList(TodoList.TABLE, TodoItem.TABLE);
public static String QUERY = ""
+ "SELECT " + LIST_ID + ", " + LIST_NAME + ", COUNT(" + ITEM_ID + ") as " + ITEM_COUNT
+ " FROM " + TodoList.TABLE + " AS " + ALIAS_LIST
+ " LEFT OUTER JOIN " + TodoItem.TABLE + " AS " + ALIAS_ITEM + " ON " + LIST_ID + " = " + ITEM_LIST_ID
+ " GROUP BY " + LIST_ID;
abstract long id();
abstract String name();
abstract int itemCount();
static Function<Cursor, ListsItem> MAPPER = new Function<Cursor, ListsItem>() {
@Override public ListsItem apply(Cursor cursor) {
long id = Db.getLong(cursor, TodoList.ID);
String name = Db.getString(cursor, TodoList.NAME);
int itemCount = Db.getInt(cursor, ITEM_COUNT);
return new AutoValue_ListsItem(id, name, itemCount);
}
};
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/MainActivity.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.ui;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import com.example.sqlbrite.todo.R;
public final class MainActivity extends FragmentActivity
implements ListsFragment.Listener, ItemsFragment.Listener {
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, ListsFragment.newInstance())
.commit();
}
}
@Override public void onListClicked(long id) {
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left,
R.anim.slide_out_right)
.replace(android.R.id.content, ItemsFragment.newInstance(id))
.addToBackStack(null)
.commit();
}
@Override public void onNewListClicked() {
NewListFragment.newInstance().show(getSupportFragmentManager(), "new-list");
}
@Override public void onNewItemClicked(long listId) {
NewItemFragment.newInstance(listId).show(getSupportFragmentManager(), "new-item");
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/NewItemFragment.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import com.example.sqlbrite.todo.R;
import com.example.sqlbrite.todo.TodoApp;
import com.example.sqlbrite.todo.db.TodoItem;
import com.jakewharton.rxbinding2.widget.RxTextView;
import com.squareup.sqlbrite3.BriteDatabase;
import io.reactivex.Observable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import javax.inject.Inject;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_NONE;
import static butterknife.ButterKnife.findById;
public final class NewItemFragment extends DialogFragment {
private static final String KEY_LIST_ID = "list_id";
public static NewItemFragment newInstance(long listId) {
Bundle arguments = new Bundle();
arguments.putLong(KEY_LIST_ID, listId);
NewItemFragment fragment = new NewItemFragment();
fragment.setArguments(arguments);
return fragment;
}
private final PublishSubject<String> createClicked = PublishSubject.create();
@Inject BriteDatabase db;
private long getListId() {
return getArguments().getLong(KEY_LIST_ID);
}
@Override public void onAttach(Activity activity) {
super.onAttach(activity);
TodoApp.getComponent(activity).inject(this);
}
@NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
View view = LayoutInflater.from(context).inflate(R.layout.new_item, null);
EditText name = findById(view, android.R.id.input);
Observable.combineLatest(createClicked, RxTextView.textChanges(name),
new BiFunction<String, CharSequence, String>() {
@Override public String apply(String ignored, CharSequence text) {
return text.toString();
}
}) //
.observeOn(Schedulers.io())
.subscribe(new Consumer<String>() {
@Override public void accept(String description) {
db.insert(TodoItem.TABLE, CONFLICT_NONE,
new TodoItem.Builder().listId(getListId()).description(description).build());
}
});
return new AlertDialog.Builder(context) //
.setTitle(R.string.new_item)
.setView(view)
.setPositiveButton(R.string.create, new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
createClicked.onNext("clicked");
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override public void onClick(@NonNull DialogInterface dialog, int which) {
}
})
.create();
}
}
================================================
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/NewListFragment.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.sqlbrite.todo.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import com.example.sqlbrite.todo.R;
import com.example.sqlbrite.todo.TodoApp;
import com.example.sqlbrite.todo.db.TodoList;
import com.jakewharton.rxbinding2.widget.RxTextView;
import com.squareup.sqlbrite3.BriteDatabase;
import io.reactivex.Observable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.PublishSubject;
import javax.inject.Inject;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_NONE;
import static butterknife.ButterKnife.findById;
public final class NewListFragment extends DialogFragment {
public static NewListFragment newInstance() {
return new NewListFragment();
}
private final PublishSubject<String> createClicked = PublishSubject.create();
@Inject BriteDatabase db;
@Override public void onAttach(Activity activity) {
super.onAttach(activity);
TodoApp.getComponent(activity).inject(this);
}
@NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) {
final Context context = getActivity();
View view = LayoutInflater.from(context).inflate(R.layout.new_list, null);
EditText name = findById(view, android.R.id.input);
Observable.combineLatest(createClicked, RxTextView.textChanges(name),
new BiFunction<String, CharSequence, String>() {
@Override public String apply(String ignored, CharSequence text) {
return text.toString();
}
}) //
.observeOn(Schedulers.io())
.subscribe(new Consumer<String>() {
@Override public void accept(String name) {
db.insert(TodoList.TABLE, CONFLICT_NONE, new TodoList.Builder().name(name).build());
}
});
return new AlertDialog.Builder(context) //
.setTitle(R.string.new_list)
.setView(view)
.setPositiveButton(R.string.create, new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int which) {
createClicked.onNext("clicked");
}
})
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
@Override public void onClick(@NonNull DialogInterface dialog, int which) {
}
})
.create();
}
}
================================================
FILE: sample/src/main/res/anim/slide_in_left.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/anim/slide_in_left.xml
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="-25%p" android:toXDelta="0"
android:duration="@android:integer/config_shortAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
================================================
FILE: sample/src/main/res/anim/slide_in_right.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/anim/slide_in_right.xml
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="25%p" android:toXDelta="0"
android:duration="@android:integer/config_shortAnimTime"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
================================================
FILE: sample/src/main/res/anim/slide_out_left.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/anim/slide_out_left.xml
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-25%p"
android:duration="@android:integer/config_shortAnimTime"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
================================================
FILE: sample/src/main/res/anim/slide_out_right.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
/* //device/apps/common/res/anim/slide_out_right.xml
**
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="25%p"
android:duration="@android:integer/config_shortAnimTime"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0"
android:duration="@android:integer/config_shortAnimTime" />
</set>
================================================
FILE: sample/src/main/res/layout/items.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<!-- TODO get nice empty view / icon -->
<ImageView
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
================================================
FILE: sample/src/main/res/layout/lists.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<!-- TODO get nice empty view / icon -->
<ImageView
android:id="@android:id/empty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
/>
<ListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
================================================
FILE: sample/src/main/res/layout/new_item.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="12dp"
>
<EditText
android:id="@android:id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapSentences|textAutoCorrect"
android:hint="@string/new_item_hint"
/>
</FrameLayout>
================================================
FILE: sample/src/main/res/layout/new_list.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="12dp"
>
<EditText
android:id="@android:id/input"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textCapWords|textAutoCorrect"
android:hint="@string/new_list_hint"
/>
</FrameLayout>
================================================
FILE: sample/src/main/res/values/strings.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">SqlBrite To-Do</string>
<string name="create">Create</string>
<string name="cancel">Cancel</string>
<string name="new_list">New List</string>
<string name="new_list_hint">Name</string>
<string name="new_item">New Item</string>
<string name="new_item_hint">Description</string>
</resources>
================================================
FILE: settings.gradle
================================================
include ':sqlbrite'
include ':sqlbrite-kotlin'
include ':sqlbrite-lint'
include ':sample'
rootProject.name = 'sqlbrite-root'
================================================
FILE: sqlbrite/build.gradle
================================================
apply plugin: 'com.android.library'
dependencies {
api rootProject.ext.rxJava
api rootProject.ext.supportSqlite
implementation rootProject.ext.supportAnnotations
androidTestImplementation rootProject.ext.supportTestRunner
androidTestImplementation rootProject.ext.truth
androidTestImplementation rootProject.ext.supportSqliteFramework
lintChecks project(':sqlbrite-lint')
}
android {
compileSdkVersion versions.compileSdk
defaultConfig {
minSdkVersion versions.minSdk
testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
lintOptions {
textOutput 'stdout'
textReport true
}
// TODO replace with https://issuetracker.google.com/issues/72050365 once released.
libraryVariants.all {
it.generateBuildConfig.enabled = false
}
}
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
================================================
FILE: sqlbrite/gradle.properties
================================================
POM_ARTIFACT_ID=sqlbrite
POM_NAME=SqlBrite
POM_PACKAGING=aar
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BlockingRecordingObserver.java
================================================
/*
* Copyright (C) 2016 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.SECONDS;
final class BlockingRecordingObserver extends RecordingObserver {
protected Object takeEvent() {
try {
Object item = events.pollFirst(1, SECONDS);
if (item == null) {
throw new AssertionError("No items.");
}
return item;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override public void assertNoMoreEvents() {
try {
assertThat(events.pollFirst(1, SECONDS)).isNull();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BriteContentResolverTest.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.test.ProviderTestCase2;
import android.test.mock.MockContentProvider;
import com.squareup.sqlbrite3.SqlBrite.Query;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.subjects.PublishSubject;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import static com.google.common.truth.Truth.assertThat;
public final class BriteContentResolverTest
extends ProviderTestCase2<BriteContentResolverTest.TestContentProvider> {
private static final Uri AUTHORITY = Uri.parse("content://test_authority");
private static final Uri TABLE = AUTHORITY.buildUpon().appendPath("test_table").build();
private static final String KEY = "test_key";
private static final String VALUE = "test_value";
private final List<String> logs = new ArrayList<>();
private final RecordingObserver o = new BlockingRecordingObserver();
private final TestScheduler scheduler = new TestScheduler();
private final PublishSubject<Object> killSwitch = PublishSubject.create();
private ContentResolver contentResolver;
private BriteContentResolver db;
public BriteContentResolverTest() {
super(TestContentProvider.class, AUTHORITY.getAuthority());
}
@Override protected void setUp() throws Exception {
super.setUp();
contentResolver = getMockContentResolver();
SqlBrite.Logger logger = new SqlBrite.Logger() {
@Override public void log(String message) {
logs.add(message);
}
};
ObservableTransformer<Query, Query> queryTransformer =
new ObservableTransformer<Query, Query>() {
@Override public ObservableSource<Query> apply(Observable<Query> upstream) {
return upstream.takeUntil(killSwitch);
}
};
db = new BriteContentResolver(contentResolver, logger, scheduler, queryTransformer);
getProvider().init(getContext().getContentResolver());
}
@Override public void tearDown() {
o.assertNoMoreEvents();
o.dispose();
}
public void testLoggerEnabled() {
db.setLoggingEnabled(true);
db.createQuery(TABLE, null, null, null, null, false).subscribe(o);
o.assertCursor().isExhausted();
contentResolver.insert(TABLE, values("key1", "value1"));
o.assertCursor().hasRow("key1", "value1").isExhausted();
assertThat(logs).isNotEmpty();
}
public void testLoggerDisabled() {
db.setLoggingEnabled(false);
contentResolver.insert(TABLE, values("key1", "value1"));
assertThat(logs).isEmpty();
}
public void testCreateQueryObservesInsert() {
db.createQuery(TABLE, null, null, null, null, false).subscribe(o);
o.assertCursor().isExhausted();
contentResolver.insert(TABLE, values("key1", "val1"));
o.assertCursor().hasRow("key1", "val1").isExhausted();
}
public void testCreateQueryObservesUpdate() {
contentResolver.insert(TABLE, values("key1", "val1"));
db.createQuery(TABLE, null, null, null, null, false).subscribe(o);
o.assertCursor().hasRow("key1", "val1").isExhausted();
contentResolver.update(TABLE, values("key1", "val2"), null, null);
o.assertCursor().hasRow("key1", "val2").isExhausted();
}
public void testCreateQueryObservesDelete() {
contentResolver.insert(TABLE, values("key1", "val1"));
db.createQuery(TABLE, null, null, null, null, false).subscribe(o);
o.assertCursor().hasRow("key1", "val1").isExhausted();
contentResolver.delete(TABLE, null, null);
o.assertCursor().isExhausted();
}
public void testUnsubscribeDoesNotTrigger() {
db.createQuery(TABLE, null, null, null, null, false).subscribe(o);
o.assertCursor().isExhausted();
o.dispose();
contentResolver.insert(TABLE, values("key1", "val1"));
o.assertNoMoreEvents();
assertThat(logs).isEmpty();
}
public void testQueryNotNotifiedWhenQueryTransformerDisposed() {
db.createQuery(TABLE, null, null, null, null, false).subscribe(o);
o.assertCursor().isExhausted();
killSwitch.onNext("kill");
o.assertIsCompleted();
contentResolver.insert(TABLE, values("key1", "val1"));
o.assertNoMoreEvents();
}
public void testInitialValueAndTriggerUsesScheduler() {
scheduler.runTasksImmediately(false);
db.createQuery(TABLE, null, null, null, null, false).subscribe(o);
o.assertNoMoreEvents();
scheduler.triggerActions();
o.assertCursor().isExhausted();
contentResolver.insert(TABLE, values("key1", "val1"));
o.assertNoMoreEvents();
scheduler.triggerActions();
o.assertCursor().hasRow("key1", "val1").isExhausted();
}
private ContentValues values(String key, String value) {
ContentValues result = new ContentValues();
result.put(KEY, key);
result.put(VALUE, value);
return result;
}
public static final class TestContentProvider extends MockContentProvider {
private final Map<String, String> storage = new LinkedHashMap<>();
private ContentResolver contentResolver;
void init(ContentResolver contentResolver) {
this.contentResolver = contentResolver;
}
@Override public Uri insert(Uri uri, ContentValues values) {
storage.put(values.getAsString(KEY), values.getAsString(VALUE));
contentResolver.notifyChange(uri, null);
return Uri.parse(AUTHORITY + "/" + values.getAsString(KEY));
}
@Override public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
for (String key : storage.keySet()) {
storage.put(key, values.getAsString(VALUE));
}
contentResolver.notifyChange(uri, null);
return storage.size();
}
@Override public int delete(Uri uri, String selection, String[] selectionArgs) {
int result = storage.size();
storage.clear();
contentResolver.notifyChange(uri, null);
return result;
}
@Override public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
MatrixCursor result = new MatrixCursor(new String[] { KEY, VALUE });
for (Map.Entry<String, String> entry : storage.entrySet()) {
result.addRow(new Object[] { entry.getKey(), entry.getValue() });
}
return result;
}
}
}
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BriteDatabaseTest.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import android.annotation.TargetApi;
import android.arch.persistence.db.SimpleSQLiteQuery;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.db.SupportSQLiteOpenHelper;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Factory;
import android.arch.persistence.db.SupportSQLiteStatement;
import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteException;
import android.os.Build;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import android.support.test.runner.AndroidJUnit4;
import com.squareup.sqlbrite3.BriteDatabase.Transaction;
import com.squareup.sqlbrite3.RecordingObserver.CursorAssert;
import com.squareup.sqlbrite3.TestDb.Employee;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.ObservableTransformer;
import io.reactivex.functions.Consumer;
import io.reactivex.subjects.PublishSubject;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_NONE;
import static com.google.common.truth.Truth.assertThat;
import static com.squareup.sqlbrite3.SqlBrite.Query;
import static com.squareup.sqlbrite3.TestDb.BOTH_TABLES;
import static com.squareup.sqlbrite3.TestDb.EmployeeTable.NAME;
import static com.squareup.sqlbrite3.TestDb.EmployeeTable.USERNAME;
import static com.squareup.sqlbrite3.TestDb.SELECT_EMPLOYEES;
import static com.squareup.sqlbrite3.TestDb.SELECT_MANAGER_LIST;
import static com.squareup.sqlbrite3.TestDb.TABLE_EMPLOYEE;
import static com.squareup.sqlbrite3.TestDb.TABLE_MANAGER;
import static com.squareup.sqlbrite3.TestDb.employee;
import static com.squareup.sqlbrite3.TestDb.manager;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class) //
public final class BriteDatabaseTest {
private final TestDb testDb = new TestDb();
private final List<String> logs = new ArrayList<>();
private final RecordingObserver o = new RecordingObserver();
private final TestScheduler scheduler = new TestScheduler();
private final PublishSubject<Object> killSwitch = PublishSubject.create();
@Rule public final TemporaryFolder dbFolder = new TemporaryFolder();
private SupportSQLiteDatabase real;
private BriteDatabase db;
@Before public void setUp() throws IOException {
Configuration configuration = Configuration.builder(InstrumentationRegistry.getContext())
.callback(testDb)
.name(dbFolder.newFile().getPath())
.build();
Factory factory = new FrameworkSQLiteOpenHelperFactory();
SupportSQLiteOpenHelper helper = factory.create(configuration);
real = helper.getWritableDatabase();
SqlBrite.Logger logger = new SqlBrite.Logger() {
@Override public void log(String message) {
logs.add(message);
}
};
ObservableTransformer<Query, Query> queryTransformer =
new ObservableTransformer<Query, Query>() {
@Override public ObservableSource<Query> apply(Observable<Query> upstream) {
return upstream.takeUntil(killSwitch);
}
};
db = new BriteDatabase(helper, logger, scheduler, queryTransformer);
}
@After public void tearDown() {
o.assertNoMoreEvents();
}
@Test public void loggerEnabled() {
db.setLoggingEnabled(true);
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
assertThat(logs).isNotEmpty();
}
@Test public void loggerDisabled() {
db.setLoggingEnabled(false);
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
assertThat(logs).isEmpty();
}
@Test public void loggerIndentsSqlForCreateQuery() {
db.setLoggingEnabled(true);
QueryObservable query = db.createQuery(TABLE_EMPLOYEE, "SELECT\n1");
query.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) throws Exception {
query.run().close();
}
});
assertThat(logs).containsExactly(""
+ "QUERY\n"
+ " tables: [employee]\n"
+ " sql: SELECT\n"
+ " 1");
}
@Test public void loggerIndentsSqlForQuery() {
db.setLoggingEnabled(true);
db.query("SELECT\n1").close();
assertThat(logs).containsExactly(""
+ "QUERY\n"
+ " sql: SELECT\n"
+ " 1\n"
+ " args: []");
}
@Test public void loggerIndentsSqlForExecute() {
db.setLoggingEnabled(true);
db.execute("PRAGMA\ncompile_options");
assertThat(logs).containsExactly(""
+ "EXECUTE\n"
+ " sql: PRAGMA\n"
+ " compile_options");
}
@Test public void loggerIndentsSqlForExecuteWithArgs() {
db.setLoggingEnabled(true);
db.execute("PRAGMA\ncompile_options", new Object[0]);
assertThat(logs).containsExactly(""
+ "EXECUTE\n"
+ " sql: PRAGMA\n"
+ " compile_options\n"
+ " args: []");
}
@Test public void closePropagates() {
db.close();
assertThat(real.isOpen()).isFalse();
}
@Test public void query() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
}
@Test public void queryWithQueryObject() {
db.createQuery(TABLE_EMPLOYEE, new SimpleSQLiteQuery(SELECT_EMPLOYEES)).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
}
@Test public void queryMapToList() {
List<Employee> employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.mapToList(Employee.MAPPER)
.blockingFirst();
assertThat(employees).containsExactly( //
new Employee("alice", "Alice Allison"), //
new Employee("bob", "Bob Bobberson"), //
new Employee("eve", "Eve Evenson"));
}
@Test public void queryMapToOne() {
Employee employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.mapToOne(Employee.MAPPER)
.blockingFirst();
assertThat(employees).isEqualTo(new Employee("alice", "Alice Allison"));
}
@Test public void queryMapToOneOrDefault() {
Employee employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.mapToOneOrDefault(Employee.MAPPER, new Employee("wrong", "Wrong Person"))
.blockingFirst();
assertThat(employees).isEqualTo(new Employee("alice", "Alice Allison"));
}
@Test public void badQueryCallsError() {
// safeSubscribe is needed because the error occurs in onNext and will otherwise bubble up
// to the thread exception handler.
db.createQuery(TABLE_EMPLOYEE, "SELECT * FROM missing").safeSubscribe(o);
o.assertErrorContains("no such table: missing");
}
@Test public void queryWithArgs() {
db.createQuery(
TABLE_EMPLOYEE, SELECT_EMPLOYEES + " WHERE " + USERNAME + " = ?", "bob")
.subscribe(o);
o.assertCursor()
.hasRow("bob", "Bob Bobberson")
.isExhausted();
}
@Test public void queryObservesInsert() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.isExhausted();
}
@Test public void queryInitialValueAndTriggerUsesScheduler() {
scheduler.runTasksImmediately(false);
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertNoMoreEvents();
scheduler.triggerActions();
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
o.assertNoMoreEvents();
scheduler.triggerActions();
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.isExhausted();
}
@Test public void queryNotNotifiedWhenInsertFails() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.insert(TABLE_EMPLOYEE, CONFLICT_IGNORE, employee("bob", "Bob Bobberson"));
o.assertNoMoreEvents();
}
@Test public void queryNotNotifiedWhenQueryTransformerUnsubscribes() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
killSwitch.onNext("kill");
o.assertIsCompleted();
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
o.assertNoMoreEvents();
}
@Test public void queryObservesUpdate() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
ContentValues values = new ContentValues();
values.put(NAME, "Robert Bobberson");
db.update(TABLE_EMPLOYEE, CONFLICT_NONE, values, USERNAME + " = 'bob'");
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Robert Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
}
@Test public void queryNotNotifiedWhenUpdateAffectsZeroRows() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
ContentValues values = new ContentValues();
values.put(NAME, "John Johnson");
db.update(TABLE_EMPLOYEE, CONFLICT_NONE, values, USERNAME + " = 'john'");
o.assertNoMoreEvents();
}
@Test public void queryObservesDelete() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.delete(TABLE_EMPLOYEE, USERNAME + " = 'bob'");
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("eve", "Eve Evenson")
.isExhausted();
}
@Test public void queryNotNotifiedWhenDeleteAffectsZeroRows() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.delete(TABLE_EMPLOYEE, USERNAME + " = 'john'");
o.assertNoMoreEvents();
}
@Test public void queryMultipleTables() {
db.createQuery(BOTH_TABLES, SELECT_MANAGER_LIST).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
}
@Test public void queryMultipleTablesWithQueryObject() {
db.createQuery(BOTH_TABLES, new SimpleSQLiteQuery(SELECT_MANAGER_LIST)).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
}
@Test public void queryMultipleTablesObservesChanges() {
db.createQuery(BOTH_TABLES, SELECT_MANAGER_LIST).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
// A new employee triggers, despite the fact that it's not in our result set.
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
// A new manager also triggers and it is in our result set.
db.insert(TABLE_MANAGER, CONFLICT_NONE, manager(testDb.bobId, testDb.eveId));
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.hasRow("Bob Bobberson", "Eve Evenson")
.isExhausted();
}
@Test public void queryMultipleTablesObservesChangesOnlyOnce() {
// Employee table is in this list twice. We should still only be notified once for a change.
List<String> tables = Arrays.asList(TABLE_EMPLOYEE, TABLE_MANAGER, TABLE_EMPLOYEE);
db.createQuery(tables, SELECT_MANAGER_LIST).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
ContentValues values = new ContentValues();
values.put(NAME, "Even Evenson");
db.update(TABLE_EMPLOYEE, CONFLICT_NONE, values, USERNAME + " = 'eve'");
o.assertCursor()
.hasRow("Even Evenson", "Alice Allison")
.isExhausted();
}
@Test public void queryNotNotifiedAfterDispose() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
o.dispose();
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
o.assertNoMoreEvents();
}
@Test public void queryOnlyNotifiedAfterSubscribe() {
Observable<Query> query = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES);
o.assertNoMoreEvents();
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
o.assertNoMoreEvents();
query.subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.isExhausted();
}
@Test public void executeSqlNoTrigger() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.skip(1) // Skip initial
.subscribe(o);
db.execute("UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = 'Zach'");
o.assertNoMoreEvents();
}
@Test public void executeSqlWithArgsNoTrigger() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.skip(1) // Skip initial
.subscribe(o);
db.execute("UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = ?", "Zach");
o.assertNoMoreEvents();
}
@Test public void executeSqlAndTrigger() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeAndTrigger(TABLE_EMPLOYEE,
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = 'Zach'");
o.assertCursor()
.hasRow("alice", "Zach")
.hasRow("bob", "Zach")
.hasRow("eve", "Zach")
.isExhausted();
}
@Test public void executeSqlAndTriggerMultipleTables() {
db.createQuery(TABLE_MANAGER, SELECT_MANAGER_LIST).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
final RecordingObserver employeeObserver = new RecordingObserver();
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(employeeObserver);
employeeObserver.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
final Set<String> tablesToTrigger = Collections.unmodifiableSet(new HashSet<>(BOTH_TABLES));
db.executeAndTrigger(tablesToTrigger,
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = 'Zach'");
o.assertCursor()
.hasRow("Zach", "Zach")
.isExhausted();
employeeObserver.assertCursor()
.hasRow("alice", "Zach")
.hasRow("bob", "Zach")
.hasRow("eve", "Zach")
.isExhausted();
}
@Test public void executeSqlAndTriggerWithNoTables() {
db.createQuery(TABLE_MANAGER, SELECT_MANAGER_LIST).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
db.executeAndTrigger(Collections.<String>emptySet(),
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = 'Zach'");
o.assertNoMoreEvents();
}
@Test public void executeSqlThrowsAndDoesNotTrigger() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.skip(1) // Skip initial
.subscribe(o);
try {
db.executeAndTrigger(TABLE_EMPLOYEE,
"UPDATE not_a_table SET " + NAME + " = 'Zach'");
fail();
} catch (SQLException ignored) {
}
o.assertNoMoreEvents();
}
@Test public void executeSqlWithArgsAndTrigger() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeAndTrigger(TABLE_EMPLOYEE,
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = ?", "Zach");
o.assertCursor()
.hasRow("alice", "Zach")
.hasRow("bob", "Zach")
.hasRow("eve", "Zach")
.isExhausted();
}
@Test public void executeSqlWithArgsThrowsAndDoesNotTrigger() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.skip(1) // Skip initial
.subscribe(o);
try {
db.executeAndTrigger(TABLE_EMPLOYEE,
"UPDATE not_a_table SET " + NAME + " = ?", "Zach");
fail();
} catch (SQLException ignored) {
}
o.assertNoMoreEvents();
}
@Test public void executeSqlWithArgsAndTriggerWithMultipleTables() {
db.createQuery(TABLE_MANAGER, SELECT_MANAGER_LIST).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
final RecordingObserver employeeObserver = new RecordingObserver();
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(employeeObserver);
employeeObserver.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
final Set<String> tablesToTrigger = Collections.unmodifiableSet(new HashSet<>(BOTH_TABLES));
db.executeAndTrigger(tablesToTrigger,
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = ?", "Zach");
o.assertCursor()
.hasRow("Zach", "Zach")
.isExhausted();
employeeObserver.assertCursor()
.hasRow("alice", "Zach")
.hasRow("bob", "Zach")
.hasRow("eve", "Zach")
.isExhausted();
}
@Test public void executeSqlWithArgsAndTriggerWithNoTables() {
db.createQuery(BOTH_TABLES, SELECT_MANAGER_LIST).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
db.executeAndTrigger(Collections.<String>emptySet(),
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = ?", "Zach");
o.assertNoMoreEvents();
}
@Test public void executeInsertAndTrigger() {
SupportSQLiteStatement statement = real.compileStatement("INSERT INTO "
+ TABLE_EMPLOYEE + " (" + NAME + ", " + USERNAME + ") "
+ "VALUES ('Chad Chadson', 'chad')");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeInsert(TABLE_EMPLOYEE, statement);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("chad", "Chad Chadson")
.isExhausted();
}
@Test public void executeInsertAndDontTrigger() {
SupportSQLiteStatement statement = real.compileStatement("INSERT OR IGNORE INTO "
+ TABLE_EMPLOYEE + " (" + NAME + ", " + USERNAME + ") "
+ "VALUES ('Alice Allison', 'alice')");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeInsert(TABLE_EMPLOYEE, statement);
o.assertNoMoreEvents();
}
@Test public void executeInsertAndTriggerMultipleTables() {
SupportSQLiteStatement statement = real.compileStatement("INSERT INTO "
+ TABLE_EMPLOYEE + " (" + NAME + ", " + USERNAME + ") "
+ "VALUES ('Chad Chadson', 'chad')");
final RecordingObserver managerObserver = new RecordingObserver();
db.createQuery(TABLE_MANAGER, SELECT_MANAGER_LIST).subscribe(managerObserver);
managerObserver.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
final Set<String> employeeAndManagerTables = Collections.unmodifiableSet(new HashSet<>(
BOTH_TABLES));
db.executeInsert(employeeAndManagerTables, statement);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("chad", "Chad Chadson")
.isExhausted();
managerObserver.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
}
@Test public void executeInsertAndTriggerNoTables() {
SupportSQLiteStatement statement = real.compileStatement("INSERT INTO "
+ TABLE_EMPLOYEE + " (" + NAME + ", " + USERNAME + ") "
+ "VALUES ('Chad Chadson', 'chad')");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeInsert(Collections.<String>emptySet(), statement);
o.assertNoMoreEvents();
}
@Test public void executeInsertThrowsAndDoesNotTrigger() {
SupportSQLiteStatement statement = real.compileStatement("INSERT INTO "
+ TABLE_EMPLOYEE + " (" + NAME + ", " + USERNAME + ") "
+ "VALUES ('Alice Allison', 'alice')");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.skip(1) // Skip initial
.subscribe(o);
try {
db.executeInsert(TABLE_EMPLOYEE, statement);
fail();
} catch (SQLException ignored) {
}
o.assertNoMoreEvents();
}
@Test public void executeInsertWithArgsAndTrigger() {
SupportSQLiteStatement statement = real.compileStatement("INSERT INTO "
+ TABLE_EMPLOYEE + " (" + NAME + ", " + USERNAME + ") VALUES (?, ?)");
statement.bindString(1, "Chad Chadson");
statement.bindString(2, "chad");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeInsert(TABLE_EMPLOYEE, statement);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("chad", "Chad Chadson")
.isExhausted();
}
@Test public void executeInsertWithArgsThrowsAndDoesNotTrigger() {
SupportSQLiteStatement statement = real.compileStatement("INSERT INTO "
+ TABLE_EMPLOYEE + " (" + NAME + ", " + USERNAME + ") VALUES (?, ?)");
statement.bindString(1, "Alice Aliison");
statement.bindString(2, "alice");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.skip(1) // Skip initial
.subscribe(o);
try {
db.executeInsert(TABLE_EMPLOYEE, statement);
fail();
} catch (SQLException ignored) {
}
o.assertNoMoreEvents();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.HONEYCOMB)
@Test public void executeUpdateDeleteAndTrigger() {
SupportSQLiteStatement statement = real.compileStatement(
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = 'Zach'");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeUpdateDelete(TABLE_EMPLOYEE, statement);
o.assertCursor()
.hasRow("alice", "Zach")
.hasRow("bob", "Zach")
.hasRow("eve", "Zach")
.isExhausted();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.HONEYCOMB)
@Test public void executeUpdateDeleteAndDontTrigger() {
SupportSQLiteStatement statement = real.compileStatement(""
+ "UPDATE " + TABLE_EMPLOYEE
+ " SET " + NAME + " = 'Zach'"
+ " WHERE " + NAME + " = 'Rob'");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeUpdateDelete(TABLE_EMPLOYEE, statement);
o.assertNoMoreEvents();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.HONEYCOMB)
@Test public void executeUpdateDeleteAndTriggerWithMultipleTables() {
SupportSQLiteStatement statement = real.compileStatement(
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = 'Zach'");
final RecordingObserver managerObserver = new RecordingObserver();
db.createQuery(TABLE_MANAGER, SELECT_MANAGER_LIST).subscribe(managerObserver);
managerObserver.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
final Set<String> employeeAndManagerTables = Collections.unmodifiableSet(new HashSet<>(BOTH_TABLES));
db.executeUpdateDelete(employeeAndManagerTables, statement);
o.assertCursor()
.hasRow("alice", "Zach")
.hasRow("bob", "Zach")
.hasRow("eve", "Zach")
.isExhausted();
managerObserver.assertCursor()
.hasRow("Zach", "Zach")
.isExhausted();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.HONEYCOMB)
@Test public void executeUpdateDeleteAndTriggerWithNoTables() {
SupportSQLiteStatement statement = real.compileStatement(
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = 'Zach'");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeUpdateDelete(Collections.<String>emptySet(), statement);
o.assertNoMoreEvents();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.HONEYCOMB)
@Test public void executeUpdateDeleteThrowsAndDoesNotTrigger() {
SupportSQLiteStatement statement = real.compileStatement(
"UPDATE " + TABLE_EMPLOYEE + " SET " + USERNAME + " = 'alice'");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.skip(1) // Skip initial
.subscribe(o);
try {
db.executeUpdateDelete(TABLE_EMPLOYEE, statement);
fail();
} catch (SQLException ignored) {
}
o.assertNoMoreEvents();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.HONEYCOMB)
@Test public void executeUpdateDeleteWithArgsAndTrigger() {
SupportSQLiteStatement statement = real.compileStatement(
"UPDATE " + TABLE_EMPLOYEE + " SET " + NAME + " = ?");
statement.bindString(1, "Zach");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.executeUpdateDelete(TABLE_EMPLOYEE, statement);
o.assertCursor()
.hasRow("alice", "Zach")
.hasRow("bob", "Zach")
.hasRow("eve", "Zach")
.isExhausted();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.HONEYCOMB)
@Test public void executeUpdateDeleteWithArgsThrowsAndDoesNotTrigger() {
SupportSQLiteStatement statement = real.compileStatement(
"UPDATE " + TABLE_EMPLOYEE + " SET " + USERNAME + " = ?");
statement.bindString(1, "alice");
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.skip(1) // Skip initial
.subscribe(o);
try {
db.executeUpdateDelete(TABLE_EMPLOYEE, statement);
fail();
} catch (SQLException ignored) {
}
o.assertNoMoreEvents();
}
@Test public void transactionOnlyNotifiesOnce() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
Transaction transaction = db.newTransaction();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("nick", "Nick Nickers"));
o.assertNoMoreEvents();
transaction.markSuccessful();
} finally {
transaction.end();
}
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.hasRow("nick", "Nick Nickers")
.isExhausted();
}
@Test public void transactionCreatedFromTransactionNotificationWorks() {
// Tests the case where a transaction is created in the subscriber to a query which gets
// notified as the result of another transaction being committed. With improper ordering, this
// can result in creating a new transaction before the old is committed on the underlying DB.
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.subscribe(new Consumer<Query>() {
@Override public void accept(Query query) {
db.newTransaction().end();
}
});
Transaction transaction = db.newTransaction();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
transaction.markSuccessful();
} finally {
transaction.end();
}
}
@Test public void transactionIsCloseable() throws IOException {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
Transaction transaction = db.newTransaction();
//noinspection UnnecessaryLocalVariable
Closeable closeableTransaction = transaction; // Verify type is implemented.
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("nick", "Nick Nickers"));
transaction.markSuccessful();
} finally {
closeableTransaction.close();
}
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.hasRow("nick", "Nick Nickers")
.isExhausted();
}
@Test public void transactionDoesNotThrow() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
Transaction transaction = db.newTransaction();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("nick", "Nick Nickers"));
transaction.markSuccessful();
} finally {
transaction.close(); // Transactions should not throw on close().
}
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.hasRow("nick", "Nick Nickers")
.isExhausted();
}
@Test public void queryCreatedDuringTransactionThrows() {
//noinspection CheckResult
db.newTransaction();
try {
//noinspection CheckResult
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES);
fail();
} catch (IllegalStateException e) {
assertThat(e.getMessage()).startsWith("Cannot create observable query in transaction.");
}
}
@Test public void querySubscribedToDuringTransactionThrows() {
Observable<Query> query = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES);
db.newTransaction();
query.subscribe(o);
o.assertErrorContains("Cannot subscribe to observable query in a transaction.");
}
@Test public void callingEndMultipleTimesThrows() {
Transaction transaction = db.newTransaction();
transaction.end();
try {
transaction.end();
fail();
} catch (IllegalStateException e) {
assertThat(e).hasMessage("Not in transaction.");
}
}
@Test public void querySubscribedToDuringTransactionOnDifferentThread()
throws InterruptedException {
Transaction transaction = db.newTransaction();
final CountDownLatch latch = new CountDownLatch(1);
new Thread() {
@Override public void run() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
latch.countDown();
}
}.start();
Thread.sleep(500); // Wait for the thread to block on initial query.
o.assertNoMoreEvents();
transaction.end(); // Allow other queries to continue.
latch.await(500, MILLISECONDS); // Wait for thread to observe initial query.
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
}
@Test public void queryCreatedBeforeTransactionButSubscribedAfter() {
Observable<Query> query = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES);
Transaction transaction = db.newTransaction();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("nick", "Nick Nickers"));
transaction.markSuccessful();
} finally {
transaction.end();
}
query.subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.hasRow("nick", "Nick Nickers")
.isExhausted();
}
@Test public void synchronousQueryDuringTransaction() {
Transaction transaction = db.newTransaction();
try {
transaction.markSuccessful();
assertCursor(db.query(SELECT_EMPLOYEES))
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
} finally {
transaction.end();
}
}
@Test public void synchronousQueryDuringTransactionSeesChanges() {
Transaction transaction = db.newTransaction();
try {
assertCursor(db.query(SELECT_EMPLOYEES))
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("nick", "Nick Nickers"));
assertCursor(db.query(SELECT_EMPLOYEES))
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.hasRow("nick", "Nick Nickers")
.isExhausted();
transaction.markSuccessful();
} finally {
transaction.end();
}
}
@Test public void synchronousQueryWithSupportSQLiteQueryDuringTransaction() {
Transaction transaction = db.newTransaction();
try {
transaction.markSuccessful();
assertCursor(db.query(new SimpleSQLiteQuery(SELECT_EMPLOYEES)))
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
} finally {
transaction.end();
}
}
@Test public void synchronousQueryWithSupportSQLiteQueryDuringTransactionSeesChanges() {
Transaction transaction = db.newTransaction();
try {
assertCursor(db.query(new SimpleSQLiteQuery(SELECT_EMPLOYEES)))
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("nick", "Nick Nickers"));
assertCursor(db.query(new SimpleSQLiteQuery(SELECT_EMPLOYEES)))
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.hasRow("nick", "Nick Nickers")
.isExhausted();
transaction.markSuccessful();
} finally {
transaction.end();
}
}
@Test public void nestedTransactionsOnlyNotifyOnce() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
Transaction transactionOuter = db.newTransaction();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
Transaction transactionInner = db.newTransaction();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("nick", "Nick Nickers"));
transactionInner.markSuccessful();
} finally {
transactionInner.end();
}
transactionOuter.markSuccessful();
} finally {
transactionOuter.end();
}
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.hasRow("john", "John Johnson")
.hasRow("nick", "Nick Nickers")
.isExhausted();
}
@Test public void nestedTransactionsOnMultipleTables() {
db.createQuery(BOTH_TABLES, SELECT_MANAGER_LIST).subscribe(o);
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.isExhausted();
Transaction transactionOuter = db.newTransaction();
try {
Transaction transactionInner = db.newTransaction();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
transactionInner.markSuccessful();
} finally {
transactionInner.end();
}
transactionInner = db.newTransaction();
try {
db.insert(TABLE_MANAGER, CONFLICT_NONE, manager(testDb.aliceId, testDb.bobId));
transactionInner.markSuccessful();
} finally {
transactionInner.end();
}
transactionOuter.markSuccessful();
} finally {
transactionOuter.end();
}
o.assertCursor()
.hasRow("Eve Evenson", "Alice Allison")
.hasRow("Alice Allison", "Bob Bobberson")
.isExhausted();
}
@Test public void emptyTransactionDoesNotNotify() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
Transaction transaction = db.newTransaction();
try {
transaction.markSuccessful();
} finally {
transaction.end();
}
o.assertNoMoreEvents();
}
@Test public void transactionRollbackDoesNotNotify() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES).subscribe(o);
o.assertCursor()
.hasRow("alice", "Alice Allison")
.hasRow("bob", "Bob Bobberson")
.hasRow("eve", "Eve Evenson")
.isExhausted();
Transaction transaction = db.newTransaction();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("john", "John Johnson"));
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("nick", "Nick Nickers"));
// No call to set successful.
} finally {
transaction.end();
}
o.assertNoMoreEvents();
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.HONEYCOMB)
@Test public void nonExclusiveTransactionWorks() throws InterruptedException {
final CountDownLatch transactionStarted = new CountDownLatch(1);
final CountDownLatch transactionProceed = new CountDownLatch(1);
final CountDownLatch transactionCompleted = new CountDownLatch(1);
new Thread() {
@Override public void run() {
Transaction transaction = db.newNonExclusiveTransaction();
transactionStarted.countDown();
try {
db.insert(TABLE_EMPLOYEE, CONFLICT_NONE, employee("hans", "Hans Hanson"));
transactionProceed.await(10, SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException("Exception in transaction thread", e);
}
transaction.markSuccessful();
transaction.close();
transactionCompleted.countDown();
}
}.start();
assertThat(transactionStarted.await(10, SECONDS)).isTrue();
//Simple query
Employee employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.lift(Query.mapToOne(Employee.MAPPER))
.blockingFirst();
assertThat(employees).isEqualTo(new Employee("alice", "Alice Allison"));
transactionProceed.countDown();
assertThat(transactionCompleted.await(10, SECONDS)).isTrue();
}
@Test public void badQueryThrows() {
try {
//noinspection CheckResult
db.query("SELECT * FROM missing");
fail();
} catch (SQLiteException e) {
assertThat(e.getMessage()).contains("no such table: missing");
}
}
@Test public void badInsertThrows() {
try {
db.insert("missing", CONFLICT_NONE, employee("john", "John Johnson"));
fail();
} catch (SQLiteException e) {
assertThat(e.getMessage()).contains("no such table: missing");
}
}
@Test public void badUpdateThrows() {
try {
db.update("missing", CONFLICT_NONE, employee("john", "John Johnson"), "1");
fail();
} catch (SQLiteException e) {
assertThat(e.getMessage()).contains("no such table: missing");
}
}
@Test public void badDeleteThrows() {
try {
db.delete("missing", "1");
fail();
} catch (SQLiteException e) {
assertThat(e.getMessage()).contains("no such table: missing");
}
}
private static CursorAssert assertCursor(Cursor cursor) {
return new CursorAssert(cursor);
}
}
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/QueryObservableTest.java
================================================
/*
* Copyright (C) 2017 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import android.database.Cursor;
import android.database.MatrixCursor;
import com.squareup.sqlbrite3.QueryObservable;
import com.squareup.sqlbrite3.SqlBrite.Query;
import io.reactivex.Observable;
import io.reactivex.functions.Function;
import org.junit.Test;
public final class QueryObservableTest {
@Test public void mapToListThrowsFromQueryRun() {
final IllegalStateException error = new IllegalStateException("test exception");
Query query = new Query() {
@Override public Cursor run() {
throw error;
}
};
new QueryObservable(Observable.just(query)) //
.mapToList(new Function<Cursor, Object>() {
@Override public Object apply(Cursor cursor) {
throw new AssertionError("Must not be called");
}
}) //
.test() //
.assertNoValues() //
.assertError(error);
}
@Test public void mapToListThrowsFromMapFunction() {
Query query = new Query() {
@Override public Cursor run() {
MatrixCursor cursor = new MatrixCursor(new String[] { "col1" });
cursor.addRow(new Object[] { "value1" });
return cursor;
}
};
final IllegalStateException error = new IllegalStateException("test exception");
new QueryObservable(Observable.just(query)) //
.mapToList(new Function<Cursor, Object>() {
@Override public Object apply(Cursor cursor) {
throw error;
}
}) //
.test() //
.assertNoValues() //
.assertError(error);
}
}
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/QueryTest.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import android.arch.persistence.db.SupportSQLiteOpenHelper;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Configuration;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Factory;
import android.arch.persistence.db.framework.FrameworkSQLiteOpenHelperFactory;
import android.database.Cursor;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SdkSuppress;
import com.squareup.sqlbrite3.SqlBrite.Query;
import com.squareup.sqlbrite3.TestDb.Employee;
import io.reactivex.Observable;
import io.reactivex.functions.Function;
import io.reactivex.observers.TestObserver;
import io.reactivex.schedulers.Schedulers;
import java.util.List;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import static com.google.common.truth.Truth.assertThat;
import static com.squareup.sqlbrite3.TestDb.Employee.MAPPER;
import static com.squareup.sqlbrite3.TestDb.SELECT_EMPLOYEES;
import static com.squareup.sqlbrite3.TestDb.TABLE_EMPLOYEE;
import static org.junit.Assert.fail;
public final class QueryTest {
private BriteDatabase db;
@Before public void setUp() {
Configuration configuration = Configuration.builder(InstrumentationRegistry.getContext())
.callback(new TestDb())
.build();
Factory factory = new FrameworkSQLiteOpenHelperFactory();
SupportSQLiteOpenHelper helper = factory.create(configuration);
SqlBrite sqlBrite = new SqlBrite.Builder().build();
db = sqlBrite.wrapDatabaseHelper(helper, Schedulers.trampoline());
}
@Test public void mapToOne() {
Employee employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.lift(Query.mapToOne(MAPPER))
.blockingFirst();
assertThat(employees).isEqualTo(new Employee("alice", "Alice Allison"));
}
@Test public void mapToOneThrowsWhenMapperReturnsNull() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.lift(Query.mapToOne(new Function<Cursor, Employee>() {
@Override public Employee apply(Cursor cursor) throws Exception {
return null;
}
}))
.test()
.assertError(NullPointerException.class)
.assertErrorMessage("QueryToOne mapper returned null");
}
@Test public void mapToOneThrowsOnMultipleRows() {
Observable<Employee> employees =
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 2") //
.lift(Query.mapToOne(MAPPER));
try {
employees.blockingFirst();
fail();
} catch (IllegalStateException e) {
assertThat(e).hasMessage("Cursor returned more than 1 row");
}
}
@Test public void mapToOneIgnoresNullCursor() {
Query nully = new Query() {
@Nullable @Override public Cursor run() {
return null;
}
};
TestObserver<Employee> observer = new TestObserver<>();
Observable.just(nully)
.lift(Query.mapToOne(MAPPER))
.subscribe(observer);
observer.assertNoValues();
observer.assertComplete();
}
@Test public void mapToOneOrDefault() {
Employee employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.lift(Query.mapToOneOrDefault(
MAPPER, new Employee("fred", "Fred Frederson")))
.blockingFirst();
assertThat(employees).isEqualTo(new Employee("alice", "Alice Allison"));
}
@Test public void mapToOneOrDefaultDisallowsNullDefault() {
try {
Query.mapToOneOrDefault(MAPPER, null);
fail();
} catch (NullPointerException e) {
assertThat(e).hasMessage("defaultValue == null");
}
}
@Test public void mapToOneOrDefaultThrowsWhenMapperReturnsNull() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.lift(Query.mapToOneOrDefault(new Function<Cursor, Employee>() {
@Override public Employee apply(Cursor cursor) throws Exception {
return null;
}
}, new Employee("fred", "Fred Frederson")))
.test()
.assertError(NullPointerException.class)
.assertErrorMessage("QueryToOne mapper returned null");
}
@Test public void mapToOneOrDefaultThrowsOnMultipleRows() {
Observable<Employee> employees =
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 2") //
.lift(Query.mapToOneOrDefault(
MAPPER, new Employee("fred", "Fred Frederson")));
try {
employees.blockingFirst();
fail();
} catch (IllegalStateException e) {
assertThat(e).hasMessage("Cursor returned more than 1 row");
}
}
@Test public void mapToOneOrDefaultReturnsDefaultWhenNullCursor() {
Employee defaultEmployee = new Employee("bob", "Bob Bobberson");
Query nully = new Query() {
@Nullable @Override public Cursor run() {
return null;
}
};
TestObserver<Employee> observer = new TestObserver<>();
Observable.just(nully)
.lift(Query.mapToOneOrDefault(MAPPER, defaultEmployee))
.subscribe(observer);
observer.assertValues(defaultEmployee);
observer.assertComplete();
}
@Test public void mapToList() {
List<Employee> employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES)
.lift(Query.mapToList(MAPPER))
.blockingFirst();
assertThat(employees).containsExactly( //
new Employee("alice", "Alice Allison"), //
new Employee("bob", "Bob Bobberson"), //
new Employee("eve", "Eve Evenson"));
}
@Test public void mapToListEmptyWhenNoRows() {
List<Employee> employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " WHERE 1=2")
.lift(Query.mapToList(MAPPER))
.blockingFirst();
assertThat(employees).isEmpty();
}
@Test public void mapToListReturnsNullOnMapperNull() {
Function<Cursor, Employee> mapToNull = new Function<Cursor, Employee>() {
private int count;
@Override public Employee apply(Cursor cursor) throws Exception {
return count++ == 2 ? null : MAPPER.apply(cursor);
}
};
List<Employee> employees = db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES) //
.lift(Query.mapToList(mapToNull)) //
.blockingFirst();
assertThat(employees).containsExactly(
new Employee("alice", "Alice Allison"),
new Employee("bob", "Bob Bobberson"),
null);
}
@Test public void mapToListIgnoresNullCursor() {
Query nully = new Query() {
@Nullable @Override public Cursor run() {
return null;
}
};
TestObserver<List<Employee>> subscriber = new TestObserver<>();
Observable.just(nully)
.lift(Query.mapToList(MAPPER))
.subscribe(subscriber);
subscriber.assertNoValues();
subscriber.assertComplete();
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
@Test public void mapToOptional() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.lift(Query.mapToOptional(MAPPER))
.test()
.assertValue(Optional.of(new Employee("alice", "Alice Allison")));
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
@Test public void mapToOptionalThrowsWhenMapperReturnsNull() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 1")
.lift(Query.mapToOptional(new Function<Cursor, Employee>() {
@Override public Employee apply(Cursor cursor) throws Exception {
return null;
}
}))
.test()
.assertError(NullPointerException.class)
.assertErrorMessage("QueryToOne mapper returned null");
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
@Test public void mapToOptionalThrowsOnMultipleRows() {
db.createQuery(TABLE_EMPLOYEE, SELECT_EMPLOYEES + " LIMIT 2") //
.lift(Query.mapToOptional(MAPPER))
.test()
.assertError(IllegalStateException.class)
.assertErrorMessage("Cursor returned more than 1 row");
}
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
@Test public void mapToOptionalIgnoresNullCursor() {
Query nully = new Query() {
@Nullable @Override public Cursor run() {
return null;
}
};
Observable.just(nully)
.lift(Query.mapToOptional(MAPPER))
.test()
.assertValue(Optional.<Employee>empty());
}
}
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/RecordingObserver.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import android.database.Cursor;
import android.util.Log;
import io.reactivex.observers.DisposableObserver;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import static com.google.common.truth.Truth.assertThat;
import static com.squareup.sqlbrite3.SqlBrite.Query;
class RecordingObserver extends DisposableObserver<Query> {
private static final Object COMPLETED = "<completed>";
private static final String TAG = RecordingObserver.class.getSimpleName();
final BlockingDeque<Object> events = new LinkedBlockingDeque<>();
@Override public final void onComplete() {
Log.d(TAG, "onCompleted");
events.add(COMPLETED);
}
@Override public final void onError(Throwable e) {
Log.d(TAG, "onError " + e.getClass().getSimpleName() + " " + e.getMessage());
events.add(e);
}
@Override public final void onNext(Query value) {
Log.d(TAG, "onNext " + value);
events.add(value.run());
}
protected Object takeEvent() {
Object item = events.removeFirst();
if (item == null) {
throw new AssertionError("No items.");
}
return item;
}
public final CursorAssert assertCursor() {
Object event = takeEvent();
assertThat(event).isInstanceOf(Cursor.class);
return new CursorAssert((Cursor) event);
}
public final void assertErrorContains(String expected) {
Object event = takeEvent();
assertThat(event).isInstanceOf(Throwable.class);
assertThat(((Throwable) event).getMessage()).contains(expected);
}
public final void assertIsCompleted() {
Object event = takeEvent();
assertThat(event).isEqualTo(COMPLETED);
}
public void assertNoMoreEvents() {
assertThat(events).isEmpty();
}
static final class CursorAssert {
private final Cursor cursor;
private int row = 0;
CursorAssert(Cursor cursor) {
this.cursor = cursor;
}
public CursorAssert hasRow(Object... values) {
assertThat(cursor.moveToNext()).named("row " + (row + 1) + " exists").isTrue();
row += 1;
assertThat(cursor.getColumnCount()).named("column count").isEqualTo(values.length);
for (int i = 0; i < values.length; i++) {
assertThat(cursor.getString(i))
.named("row " + row + " column '" + cursor.getColumnName(i) + "'")
.isEqualTo(values[i]);
}
return this;
}
public void isExhausted() {
if (cursor.moveToNext()) {
StringBuilder data = new StringBuilder();
for (int i = 0; i < cursor.getColumnCount(); i++) {
if (i > 0) data.append(", ");
data.append(cursor.getString(i));
}
throw new AssertionError("Expected no more rows but was: " + data);
}
cursor.close();
}
}
}
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/SqlBriteTest.java
================================================
package com.squareup.sqlbrite3;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.support.annotation.Nullable;
import android.support.test.runner.AndroidJUnit4;
import com.squareup.sqlbrite3.SqlBrite.Query;
import io.reactivex.functions.Function;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.junit.runner.RunWith;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
@RunWith(AndroidJUnit4.class)
@SuppressWarnings("CheckResult")
public final class SqlBriteTest {
private static final String FIRST_NAME = "first_name";
private static final String LAST_NAME = "last_name";
private static final String[] COLUMN_NAMES = { FIRST_NAME, LAST_NAME };
@Test public void builderDisallowsNull() {
SqlBrite.Builder builder = new SqlBrite.Builder();
try {
builder.logger(null);
fail();
} catch (NullPointerException e) {
assertThat(e).hasMessage("logger == null");
}
try {
builder.queryTransformer(null);
fail();
} catch (NullPointerException e) {
assertThat(e).hasMessage("queryTransformer == null");
}
}
@Test public void asRowsEmpty() {
MatrixCursor cursor = new MatrixCursor(COLUMN_NAMES);
Query query = new CursorQuery(cursor);
List<Name> names = query.asRows(Name.MAP).toList().blockingGet();
assertThat(names).isEmpty();
}
@Test public void asRows() {
MatrixCursor cursor = new MatrixCursor(COLUMN_NAMES);
cursor.addRow(new Object[] { "Alice", "Allison" });
cursor.addRow(new Object[] { "Bob", "Bobberson" });
Query query = new CursorQuery(cursor);
List<Name> names = query.asRows(Name.MAP).toList().blockingGet();
assertThat(names).containsExactly(new Name("Alice", "Allison"), new Name("Bob", "Bobberson"));
}
@Test public void asRowsStopsWhenUnsubscribed() {
MatrixCursor cursor = new MatrixCursor(COLUMN_NAMES);
cursor.addRow(new Object[] { "Alice", "Allison" });
cursor.addRow(new Object[] { "Bob", "Bobberson" });
Query query = new CursorQuery(cursor);
final AtomicInteger count = new AtomicInteger();
query.asRows(new Function<Cursor, Name>() {
@Override public Name apply(Cursor cursor) throws Exception {
count.incrementAndGet();
return Name.MAP.apply(cursor);
}
}).take(1).blockingFirst();
assertThat(count.get()).isEqualTo(1);
}
@Test public void asRowsEmptyWhenNullCursor() {
Query nully = new Query() {
@Nullable @Override public Cursor run() {
return null;
}
};
final AtomicInteger count = new AtomicInteger();
nully.asRows(new Function<Cursor, Name>() {
@Override public Name apply(Cursor cursor) throws Exception {
count.incrementAndGet();
return Name.MAP.apply(cursor);
}
}).test().assertNoValues().assertComplete();
assertThat(count.get()).isEqualTo(0);
}
static final class Name {
static final Function<Cursor, Name> MAP = new Function<Cursor, Name>() {
@Override public Name apply(Cursor cursor) {
return new Name( //
cursor.getString(cursor.getColumnIndexOrThrow(FIRST_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(LAST_NAME)));
}
};
final String first;
final String last;
Name(String first, String last) {
this.first = first;
this.last = last;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Name)) return false;
Name other = (Name) o;
return first.equals(other.first) && last.equals(other.last);
}
@Override public int hashCode() {
return first.hashCode() * 17 + last.hashCode();
}
@Override public String toString() {
return "Name[" + first + ' ' + last + ']';
}
}
static final class CursorQuery extends Query {
private final Cursor cursor;
CursorQuery(Cursor cursor) {
this.cursor = cursor;
}
@Override public Cursor run() {
return cursor;
}
}
}
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/TestDb.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.db.SupportSQLiteOpenHelper;
import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.NonNull;
import io.reactivex.functions.Function;
import java.util.Arrays;
import java.util.Collection;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL;
import static com.squareup.sqlbrite3.TestDb.EmployeeTable.ID;
import static com.squareup.sqlbrite3.TestDb.EmployeeTable.NAME;
import static com.squareup.sqlbrite3.TestDb.EmployeeTable.USERNAME;
import static com.squareup.sqlbrite3.TestDb.ManagerTable.EMPLOYEE_ID;
import static com.squareup.sqlbrite3.TestDb.ManagerTable.MANAGER_ID;
final class TestDb extends SupportSQLiteOpenHelper.Callback {
static final String TABLE_EMPLOYEE = "employee";
static final String TABLE_MANAGER = "manager";
static final String SELECT_EMPLOYEES =
"SELECT " + USERNAME + ", " + NAME + " FROM " + TABLE_EMPLOYEE;
static final String SELECT_MANAGER_LIST = ""
+ "SELECT e." + NAME + ", m." + NAME + " "
+ "FROM " + TABLE_MANAGER + " AS manager "
+ "JOIN " + TABLE_EMPLOYEE + " AS e "
+ "ON manager." + EMPLOYEE_ID + " = e." + ID + " "
+ "JOIN " + TABLE_EMPLOYEE + " as m "
+ "ON manager." + MANAGER_ID + " = m." + ID;
static final Collection<String> BOTH_TABLES =
Arrays.asList(TABLE_EMPLOYEE, TABLE_MANAGER);
interface EmployeeTable {
String ID = "_id";
String USERNAME = "username";
String NAME = "name";
}
static final class Employee {
static final Function<Cursor, Employee> MAPPER = new Function<Cursor, Employee>() {
@Override public Employee apply(Cursor cursor) {
return new Employee( //
cursor.getString(cursor.getColumnIndexOrThrow(EmployeeTable.USERNAME)),
cursor.getString(cursor.getColumnIndexOrThrow(EmployeeTable.NAME)));
}
};
final String username;
final String name;
Employee(String username, String name) {
this.username = username;
this.name = name;
}
@Override public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Employee)) return false;
Employee other = (Employee) o;
return username.equals(other.username) && name.equals(other.name);
}
@Override public int hashCode() {
return username.hashCode() * 17 + name.hashCode();
}
@Override public String toString() {
return "Employee[" + username + ' ' + name + ']';
}
}
interface ManagerTable {
String ID = "_id";
String EMPLOYEE_ID = "employee_id";
String MANAGER_ID = "manager_id";
}
private static final String CREATE_EMPLOYEE = "CREATE TABLE " + TABLE_EMPLOYEE + " ("
+ EmployeeTable.ID + " INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
+ EmployeeTable.USERNAME + " TEXT NOT NULL UNIQUE, "
+ EmployeeTable.NAME + " TEXT NOT NULL)";
private static final String CREATE_MANAGER = "CREATE TABLE " + TABLE_MANAGER + " ("
+ ManagerTable.ID + " INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "
+ ManagerTable.EMPLOYEE_ID + " INTEGER NOT NULL UNIQUE REFERENCES " + TABLE_EMPLOYEE + "(" + EmployeeTable.ID + "), "
+ ManagerTable.MANAGER_ID + " INTEGER NOT NULL REFERENCES " + TABLE_EMPLOYEE + "(" + EmployeeTable.ID + "))";
long aliceId;
long bobId;
long eveId;
TestDb() {
super(1);
}
@Override public void onCreate(@NonNull SupportSQLiteDatabase db) {
db.execSQL("PRAGMA foreign_keys=ON");
db.execSQL(CREATE_EMPLOYEE);
aliceId = db.insert(TABLE_EMPLOYEE, CONFLICT_FAIL, employee("alice", "Alice Allison"));
bobId = db.insert(TABLE_EMPLOYEE, CONFLICT_FAIL, employee("bob", "Bob Bobberson"));
eveId = db.insert(TABLE_EMPLOYEE, CONFLICT_FAIL, employee("eve", "Eve Evenson"));
db.execSQL(CREATE_MANAGER);
db.insert(TABLE_MANAGER, CONFLICT_FAIL, manager(eveId, aliceId));
}
static ContentValues employee(String username, String name) {
ContentValues values = new ContentValues();
values.put(EmployeeTable.USERNAME, username);
values.put(EmployeeTable.NAME, name);
return values;
}
static ContentValues manager(long employeeId, long managerId) {
ContentValues values = new ContentValues();
values.put(ManagerTable.EMPLOYEE_ID, employeeId);
values.put(ManagerTable.MANAGER_ID, managerId);
return values;
}
@Override
public void onUpgrade(@NonNull SupportSQLiteDatabase db, int oldVersion, int newVersion) {
throw new AssertionError();
}
}
================================================
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/TestScheduler.java
================================================
/*
* Copyright (C) 2016 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import io.reactivex.Scheduler;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import java.util.concurrent.TimeUnit;
final class TestScheduler extends Scheduler {
private final io.reactivex.schedulers.TestScheduler delegate =
new io.reactivex.schedulers.TestScheduler();
private boolean runTasksImmediately = true;
public void runTasksImmediately(boolean runTasksImmediately) {
this.runTasksImmediately = runTasksImmediately;
}
public void triggerActions() {
delegate.triggerActions();
}
@Override public Worker createWorker() {
return new TestWorker();
}
class TestWorker extends Worker {
private final Worker delegateWorker = delegate.createWorker();
@Override
public Disposable schedule(@NonNull Runnable run, long delay, @NonNull TimeUnit unit) {
Disposable disposable = delegateWorker.schedule(run, delay, unit);
if (runTasksImmediately) {
triggerActions();
}
return disposable;
}
@Override public void dispose() {
delegateWorker.dispose();
}
@Override public boolean isDisposed() {
return delegateWorker.isDisposed();
}
}
}
================================================
FILE: sqlbrite/src/main/AndroidManifest.xml
================================================
<manifest package="com.squareup.sqlbrite2"/>
================================================
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/BriteContentResolver.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.CheckResult;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import com.squareup.sqlbrite3.SqlBrite.Logger;
import com.squareup.sqlbrite3.SqlBrite.Query;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableTransformer;
import io.reactivex.Scheduler;
import io.reactivex.functions.Cancellable;
import java.util.Arrays;
import static com.squareup.sqlbrite3.QueryObservable.QUERY_OBSERVABLE;
import static java.lang.System.nanoTime;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* A lightweight wrapper around {@link ContentResolver} which allows for continuously observing
* the result of a query. Create using a {@link SqlBrite} instance.
*/
public final class BriteContentResolver {
final Handler contentObserverHandler = new Handler(Looper.getMainLooper());
final ContentResolver contentResolver;
private final Logger logger;
private final Scheduler scheduler;
private final ObservableTransformer<Query, Query> queryTransformer;
volatile boolean logging;
BriteContentResolver(ContentResolver contentResolver, Logger logger, Scheduler scheduler,
ObservableTransformer<Query, Query> queryTransformer) {
this.contentResolver = contentResolver;
this.logger = logger;
this.scheduler = scheduler;
this.queryTransformer = queryTransformer;
}
/** Control whether debug logging is enabled. */
public void setLoggingEnabled(boolean enabled) {
logging = enabled;
}
/**
* Create an observable which will notify subscribers with a {@linkplain Query query} for
* execution. Subscribers are responsible for <b>always</b> closing {@link Cursor} instance
* returned from the {@link Query}.
* <p>
* Subscribers will receive an immediate notification for initial data as well as subsequent
* notifications for when the supplied {@code uri}'s data changes. Unsubscribe when you no longer
* want updates to a query.
* <p>
* Since content resolver triggers are inherently asynchronous, items emitted from the returned
* observable use the {@link Scheduler} supplied to {@link SqlBrite#wrapContentProvider}. For
* consistency, the immediate notification sent on subscribe also uses this scheduler. As such,
* calling {@link Observable#subscribeOn subscribeOn} on the returned observable has no effect.
* <p>
* Note: To skip the immediate notification and only receive subsequent notifications when data
* has changed call {@code skip(1)} on the returned observable.
* <p>
* <b>Warning:</b> this method does not perform the query! Only by subscribing to the returned
* {@link Observable} will the operation occur.
*
* @see ContentResolver#query(Uri, String[], String, String[], String)
* @see ContentResolver#registerContentObserver(Uri, boolean, ContentObserver)
*/
@CheckResult @NonNull
public QueryObservable createQuery(@NonNull final Uri uri, @Nullable final String[] projection,
@Nullable final String selection, @Nullable final String[] selectionArgs, @Nullable
final String sortOrder, final boolean notifyForDescendents) {
final Query query = new Query() {
@Override public Cursor run() {
long startNanos = nanoTime();
Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, sortOrder);
if (logging) {
long tookMillis = NANOSECONDS.toMillis(nanoTime() - startNanos);
log("QUERY (%sms)\n uri: %s\n projection: %s\n selection: %s\n selectionArgs: %s\n "
+ "sortOrder: %s\n notifyForDescendents: %s", tookMillis, uri,
Arrays.toString(projection), selection, Arrays.toString(selectionArgs), sortOrder,
notifyForDescendents);
}
return cursor;
}
};
Observable<Query> queries = Observable.create(new ObservableOnSubscribe<Query>() {
@Override public void subscribe(final ObservableEmitter<Query> e) throws Exception {
final ContentObserver observer = new ContentObserver(contentObserverHandler) {
@Override public void onChange(boolean selfChange) {
if (!e.isDisposed()) {
e.onNext(query);
}
}
};
contentResolver.registerContentObserver(uri, notifyForDescendents, observer);
e.setCancellable(new Cancellable() {
@Override public void cancel() throws Exception {
contentResolver.unregisterContentObserver(observer);
}
});
if (!e.isDisposed()) {
e.onNext(query); // Trigger initial query.
}
}
});
return queries //
.observeOn(scheduler) //
.compose(queryTransformer) // Apply the user's query transformer.
.to(QUERY_OBSERVABLE);
}
void log(String message, Object... args) {
if (args.length > 0) message = String.format(message, args);
logger.log(message);
}
}
================================================
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/BriteDatabase.java
================================================
/*
* Copyright (C) 2015 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.squareup.sqlbrite3;
import android.arch.persistence.db.SimpleSQLiteQuery;
import android.arch.persistence.db.SupportSQLiteDatabase;
import android.arch.persistence.db.SupportSQLiteOpenHelper;
import android.arch.persistence.db.SupportSQLiteOpenHelper.Callback;
import android.arch.persistence.db.SupportSQLiteQuery;
import android.arch.persistence.db.SupportSQLiteStatement;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteTransactionListener;
import android.support.annotation.CheckResult;
import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.WorkerThread;
import com.squareup.sqlbrite3.SqlBrite.Logger;
import com.squareup.sqlbrite3.SqlBrite.Query;
import io.reactivex.Observable;
import io.reactivex.ObservableTransformer;
import io.reactivex.Scheduler;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.functions.Predicate;
import io.reactivex.subjects.PublishSubject;
import io.reactivex.subjects.Subject;
import java.io.Closeable;
import java.lang.annotation.Retention;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_ABORT;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_FAIL;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_NONE;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_REPLACE;
import static android.database.sqlite.SQLiteDatabase.CONFLICT_ROLLBACK;
import static com.squareup.sqlbrite3.QueryObservable.QUERY_OBSERVABLE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static java.util.Collections.singletonList;
/**
* A lightweight wrapper around {@link SupportSQLiteOpenHelper} which allows for continuously
* observing the result of a query. Create using a {@link SqlBrite} instance.
*/
public final class BriteDatabase implements Closeable {
private final SupportSQLiteOpenHelper helper;
private final Logger logger;
private final ObservableTransformer<Query, Query> queryTransformer;
// Package-private to avoid synthetic accessor method for 'transaction' instance.
final ThreadLocal<SqliteTransaction> transactions = new ThreadLocal<>();
private final Subject<Set<String>> triggers = PublishSubject.create();
private final Transaction transaction = new Transaction() {
@Override public void markSuccessful() {
if (logging) log("TXN SUCCESS %s", transactions.get());
getWritableDatabase().setTransactionSuccessful();
}
@Override public boolean yieldIfContendedSafely() {
return getWritableDatabase().yieldIfContendedSafely();
}
@Override public boolean yieldIfContendedSafely(long sleepAmount, TimeUnit sleepUnit) {
return getWritableDatabase().yieldIfContendedSafely(sleepUnit.toMillis(sleepAmount));
}
@Override public void end() {
SqliteTransaction transaction = transactions.get();
if (transaction == null) {
throw new IllegalStateException("Not in transaction.");
}
SqliteTransaction newTransaction = transaction.parent;
transactions.set(newTransaction);
if (logging) log("TXN END %s", transaction);
getWritableDatabase().endTransaction();
// Send the triggers after ending the transaction in the DB.
if (transaction.commit) {
sendTableTrigger(transaction);
}
}
@Override public void close() {
end();
}
};
private final Consumer<Object> ensureNotInTransaction = new Consumer<Object>() {
@Override public void accept(Object ignored) throws Exception {
if (transactions.get() != null) {
throw new IllegalStateException("Cannot subscribe to observable query in a transaction.");
}
}
};
private final Scheduler scheduler;
// Package-private to avoid synthetic accessor method for 'transaction' instance.
volatile boolean logging;
BriteDatabase(SupportSQLiteOpenHelper helper, Logger logger, Scheduler scheduler,
ObservableTransformer<Query, Query> queryTransformer) {
this.helper = helper;
this.logger = logger;
this.scheduler = scheduler;
this.queryTransformer = queryTransformer;
}
/**
* Control whether debug logging is enabled.
*/
public void setLoggingEnabled(boolean enabled) {
logging = enabled;
}
/**
* Create and/or open a database. This will be the same object returned by
* {@link SupportSQLiteOpenHelper#getWritableDatabase} unless some problem, such as a full disk,
* requires the database to be opened read-only. In that case, a read-only
* database object will be returned. If the problem is fixed, a future call
* to {@link SupportSQLiteOpenHelper#getWritableDatabase} may succeed, in which case the read-only
* database object will be closed and the read/write object will be returned
* in the future.
*
* <p class="caution">Like {@link SupportSQLiteOpenHelper#getWritableDatabase}, this method may
* take a long time to return, so you should not call it from the
* application main thread, including from
* {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
*
* @throws android.database.sqlite.SQLiteException if the database cannot be opened
* @return a database object valid until {@link SupportSQLiteOpenHelper#getWritableDatabase}
* or {@link #close} is called.
*/
@NonNull @CheckResult @WorkerThread
public SupportSQLiteDatabase getReadableDatabase() {
return helper.getReadableDatabase();
}
/**
* Create and/or open a database that will be used for reading and writing.
* The first time this is called, the database will be opened and
* {@link Callback#onCreate}, {@link Callback#onUpgrade} and/or {@link Callback#onOpen} will be
* called.
*
* <p>Once opened successfully, the database is cached, so you can
* call this method every time you need to write to the database.
* (Make sure to call {@link #close} when you no longer need the database.)
* Errors such as bad permissions or a full disk may cause this method
* to fail, but future attempts may succeed if the problem is fixed.</p>
*
* <p class="caution">Database upgrade may take a long time, you
* should not call this method from the application main thread, including
* from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
*
* @throws android.database.sqlite.SQLiteException if the database cannot be opened for writing
* @return a read/write database object valid until {@link #close} is called
*/
@NonNull @CheckResult @WorkerThread
public SupportSQLiteDatabase getWritableDatabase() {
return helper.getWritableDatabase();
}
void sendTableTrigger(Set<String> tables) {
SqliteTransaction transaction = transactions.get();
if (transaction != null) {
transaction.addAll(tables);
} else {
if (logging) log("TRIGGER %s", tables);
triggers.onNext(tables);
}
}
/**
* Begin a transaction for this thread.
* <p>
* Transactions may nest. If the transaction is not in progress, then a database connection is
* obtained and a new transaction is started. Otherwise, a nested transaction is started.
* <p>
* Each call to {@code newTransaction} must be matched exactly by a call to
* {@link Transaction#end()}. To mark a transaction as successful, call
* {@link Transaction#markSuccessful()} before calling {@link Transaction#end()}. If the
* transaction is not successful, or if any of its nested transactions were not successful, then
* the entire transaction will be rolled back when the outermost transaction is ended.
* <p>
* Transactions queue up all query notifications until they have been applied.
* <p>
* Here is the standard idiom for transactions:
*
* <pre>{@code
* try (Transaction transaction = db.newTransaction()) {
* ...
* transaction.markSuccessful();
* }
* }</pre>
*
* Manually call {@link Transaction#end()} when try-with-resources is not available:
* <pre>{@code
* Transaction transaction = db.newTransaction();
* try {
* ...
* transaction.markSuccessful();
* } finally {
* transaction.end();
* }
* }</pre>
*
*
* @see SupportSQLiteDatabase#beginTransaction()
*/
@CheckResult @NonNull
public Transaction newTransaction() {
SqliteTransaction transaction = new SqliteTransaction(transactions.get());
transactions.set(transaction);
if (logging) log("TXN BEGIN %s", transaction);
getWritableDatabase().beginTransactionWithListener(transaction);
return this.transaction;
}
/**
* Begins a transaction in IMMEDIATE mode for this thread.
* <p>
* Transactions may nest. If the transaction is not in progress, then a database connection is
* obtained and a new transaction is started. Otherwise, a nested transaction is started.
* <p>
* Each call to {@code newNonExclusiveTransaction} must be matched exactly by a call to
* {@link Transaction#end()}. To mark a transaction as successful, call
* {@link Transaction#markSuccessful()} before calling {@link Transaction#end()}. If the
* transaction is not successful, or if any of its nested transactions were not successful, then
* the entire transaction will be rolled back when the outermost transaction is ended.
*
gitextract_8j63fi6p/
├── .buildscript/
│ └── deploy_snapshot.sh
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── RELEASING.md
├── build.gradle
├── gradle/
│ ├── gradle-mvn-push.gradle
│ └── wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradle.properties
├── gradlew
├── sample/
│ ├── build.gradle
│ ├── debug.keystore
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ ├── java/
│ │ └── com/
│ │ └── example/
│ │ └── sqlbrite/
│ │ └── todo/
│ │ ├── TodoApp.java
│ │ ├── TodoComponent.java
│ │ ├── TodoModule.java
│ │ ├── db/
│ │ │ ├── Db.java
│ │ │ ├── DbCallback.java
│ │ │ ├── DbModule.java
│ │ │ ├── TodoItem.java
│ │ │ └── TodoList.java
│ │ └── ui/
│ │ ├── ItemsAdapter.java
│ │ ├── ItemsFragment.java
│ │ ├── ListsAdapter.java
│ │ ├── ListsFragment.java
│ │ ├── ListsItem.java
│ │ ├── MainActivity.java
│ │ ├── NewItemFragment.java
│ │ └── NewListFragment.java
│ └── res/
│ ├── anim/
│ │ ├── slide_in_left.xml
│ │ ├── slide_in_right.xml
│ │ ├── slide_out_left.xml
│ │ └── slide_out_right.xml
│ ├── layout/
│ │ ├── items.xml
│ │ ├── lists.xml
│ │ ├── new_item.xml
│ │ └── new_list.xml
│ └── values/
│ └── strings.xml
├── settings.gradle
├── sqlbrite/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ ├── androidTest/
│ │ └── java/
│ │ └── com/
│ │ └── squareup/
│ │ └── sqlbrite3/
│ │ ├── BlockingRecordingObserver.java
│ │ ├── BriteContentResolverTest.java
│ │ ├── BriteDatabaseTest.java
│ │ ├── QueryObservableTest.java
│ │ ├── QueryTest.java
│ │ ├── RecordingObserver.java
│ │ ├── SqlBriteTest.java
│ │ ├── TestDb.java
│ │ └── TestScheduler.java
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── squareup/
│ └── sqlbrite3/
│ ├── BriteContentResolver.java
│ ├── BriteDatabase.java
│ ├── QueryObservable.java
│ ├── QueryToListOperator.java
│ ├── QueryToOneOperator.java
│ ├── QueryToOptionalOperator.java
│ └── SqlBrite.java
├── sqlbrite-kotlin/
│ ├── build.gradle
│ ├── gradle.properties
│ └── src/
│ └── main/
│ ├── AndroidManifest.xml
│ └── java/
│ └── com/
│ └── squareup/
│ └── sqlbrite3/
│ └── extensions.kt
└── sqlbrite-lint/
├── build.gradle
└── src/
├── main/
│ └── java/
│ └── com/
│ └── squareup/
│ └── sqlbrite3/
│ ├── BriteIssueRegistry.kt
│ └── SqlBriteArgCountDetector.kt
└── test/
└── java/
└── com/
└── squareup/
└── sqlbrite3/
└── SqlBriteArgCountDetectorTest.kt
SYMBOL INDEX (390 symbols across 32 files)
FILE: sample/src/main/java/com/example/sqlbrite/todo/TodoApp.java
class TodoApp (line 22) | public final class TodoApp extends Application {
method onCreate (line 25) | @Override public void onCreate() {
method getComponent (line 35) | public static TodoComponent getComponent(Context context) {
FILE: sample/src/main/java/com/example/sqlbrite/todo/TodoComponent.java
type TodoComponent (line 25) | @Singleton
method inject (line 29) | void inject(ListsFragment fragment);
method inject (line 31) | void inject(ItemsFragment fragment);
method inject (line 33) | void inject(NewItemFragment fragment);
method inject (line 35) | void inject(NewListFragment fragment);
FILE: sample/src/main/java/com/example/sqlbrite/todo/TodoModule.java
class TodoModule (line 24) | @Module(
method TodoModule (line 32) | TodoModule(Application application) {
method provideApplication (line 36) | @Provides @Singleton Application provideApplication() {
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/Db.java
class Db (line 20) | public final class Db {
method getString (line 24) | public static String getString(Cursor cursor, String columnName) {
method getBoolean (line 28) | public static boolean getBoolean(Cursor cursor, String columnName) {
method getLong (line 32) | public static long getLong(Cursor cursor, String columnName) {
method getInt (line 36) | public static int getInt(Cursor cursor, String columnName) {
method Db (line 40) | private Db() {
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/DbCallback.java
class DbCallback (line 26) | final class DbCallback extends SupportSQLiteOpenHelper.Callback {
method DbCallback (line 45) | DbCallback() {
method onCreate (line 49) | @Override public void onCreate(SupportSQLiteDatabase db) {
method onUpgrade (line 117) | @Override public void onUpgrade(SupportSQLiteDatabase db, int oldVersi...
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/DbModule.java
class DbModule (line 31) | @Module
method provideSqlBrite (line 33) | @Provides @Singleton SqlBrite provideSqlBrite() {
method provideDatabase (line 43) | @Provides @Singleton BriteDatabase provideDatabase(SqlBrite sqlBrite, ...
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/TodoItem.java
class TodoItem (line 24) | @AutoValue
method id (line 33) | public abstract long id();
method listId (line 34) | public abstract long listId();
method description (line 35) | public abstract String description();
method complete (line 36) | public abstract boolean complete();
method apply (line 39) | @Override public TodoItem apply(Cursor cursor) {
class Builder (line 48) | public static final class Builder {
method id (line 51) | public Builder id(long id) {
method listId (line 56) | public Builder listId(long listId) {
method description (line 61) | public Builder description(String description) {
method complete (line 66) | public Builder complete(boolean complete) {
method build (line 71) | public ContentValues build() {
FILE: sample/src/main/java/com/example/sqlbrite/todo/db/TodoList.java
class TodoList (line 23) | @AutoValue
method id (line 31) | public abstract long id();
method name (line 32) | public abstract String name();
method archived (line 33) | public abstract boolean archived();
class Builder (line 35) | public static final class Builder {
method id (line 38) | public Builder id(long id) {
method name (line 43) | public Builder name(String name) {
method archived (line 48) | public Builder archived(boolean archived) {
method build (line 53) | public ContentValues build() {
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ItemsAdapter.java
class ItemsAdapter (line 31) | final class ItemsAdapter extends BaseAdapter implements Consumer<List<To...
method ItemsAdapter (line 36) | public ItemsAdapter(Context context) {
method accept (line 40) | @Override public void accept(List<TodoItem> items) {
method getCount (line 45) | @Override public int getCount() {
method getItem (line 49) | @Override public TodoItem getItem(int position) {
method getItemId (line 53) | @Override public long getItemId(int position) {
method hasStableIds (line 57) | @Override public boolean hasStableIds() {
method getView (line 61) | @Override public View getView(int position, View convertView, ViewGrou...
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ItemsFragment.java
class ItemsFragment (line 55) | public final class ItemsFragment extends Fragment {
type Listener (line 76) | public interface Listener {
method onNewItemClicked (line 77) | void onNewItemClicked(long listId);
method newInstance (line 80) | public static ItemsFragment newInstance(long listId) {
method getListId (line 98) | private long getListId() {
method onAttach (line 102) | @Override public void onAttach(Activity activity) {
method onCreateOptionsMenu (line 115) | @Override public void onCreateOptionsMenu(Menu menu, MenuInflater infl...
method onCreateView (line 128) | @Override public View onCreateView(LayoutInflater inflater, @Nullable ...
method onViewCreated (line 133) | @Override
method onResume (line 152) | @Override public void onResume() {
method onPause (line 205) | @Override public void onPause() {
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ListsAdapter.java
class ListsAdapter (line 28) | final class ListsAdapter extends BaseAdapter implements Consumer<List<Li...
method ListsAdapter (line 33) | public ListsAdapter(Context context) {
method accept (line 37) | @Override public void accept(List<ListsItem> items) {
method getCount (line 42) | @Override public int getCount() {
method getItem (line 46) | @Override public ListsItem getItem(int position) {
method getItemId (line 50) | @Override public long getItemId(int position) {
method hasStableIds (line 54) | @Override public boolean hasStableIds() {
method getView (line 58) | @Override public View getView(int position, View convertView, ViewGrou...
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ListsFragment.java
class ListsFragment (line 43) | public final class ListsFragment extends Fragment {
type Listener (line 44) | interface Listener {
method onListClicked (line 45) | void onListClicked(long id);
method onNewListClicked (line 46) | void onNewListClicked();
method newInstance (line 49) | static ListsFragment newInstance() {
method onAttach (line 62) | @Override public void onAttach(Activity activity) {
method onCreateOptionsMenu (line 75) | @Override public void onCreateOptionsMenu(Menu menu, MenuInflater infl...
method onCreateView (line 88) | @Override public View onCreateView(LayoutInflater inflater, @Nullable ...
method onViewCreated (line 93) | @Override
method listClicked (line 101) | @OnItemClick(android.R.id.list) void listClicked(long listId) {
method onResume (line 105) | @Override public void onResume() {
method onPause (line 116) | @Override public void onPause() {
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/ListsItem.java
class ListsItem (line 28) | @AutoValue
method id (line 46) | abstract long id();
method name (line 47) | abstract String name();
method itemCount (line 48) | abstract int itemCount();
method apply (line 51) | @Override public ListsItem apply(Cursor cursor) {
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/MainActivity.java
class MainActivity (line 22) | public final class MainActivity extends FragmentActivity
method onCreate (line 25) | @Override protected void onCreate(Bundle savedInstanceState) {
method onListClicked (line 34) | @Override public void onListClicked(long id) {
method onNewListClicked (line 43) | @Override public void onNewListClicked() {
method onNewItemClicked (line 47) | @Override public void onNewItemClicked(long listId) {
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/NewItemFragment.java
class NewItemFragment (line 44) | public final class NewItemFragment extends DialogFragment {
method newInstance (line 47) | public static NewItemFragment newInstance(long listId) {
method getListId (line 60) | private long getListId() {
method onAttach (line 64) | @Override public void onAttach(Activity activity) {
method onCreateDialog (line 69) | @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceSt...
FILE: sample/src/main/java/com/example/sqlbrite/todo/ui/NewListFragment.java
class NewListFragment (line 44) | public final class NewListFragment extends DialogFragment {
method newInstance (line 45) | public static NewListFragment newInstance() {
method onAttach (line 53) | @Override public void onAttach(Activity activity) {
method onCreateDialog (line 58) | @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceSt...
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BlockingRecordingObserver.java
class BlockingRecordingObserver (line 21) | final class BlockingRecordingObserver extends RecordingObserver {
method takeEvent (line 22) | protected Object takeEvent() {
method assertNoMoreEvents (line 34) | @Override public void assertNoMoreEvents() {
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BriteContentResolverTest.java
class BriteContentResolverTest (line 37) | public final class BriteContentResolverTest
method BriteContentResolverTest (line 52) | public BriteContentResolverTest() {
method setUp (line 56) | @Override protected void setUp() throws Exception {
method tearDown (line 76) | @Override public void tearDown() {
method testLoggerEnabled (line 81) | public void testLoggerEnabled() {
method testLoggerDisabled (line 92) | public void testLoggerDisabled() {
method testCreateQueryObservesInsert (line 99) | public void testCreateQueryObservesInsert() {
method testCreateQueryObservesUpdate (line 107) | public void testCreateQueryObservesUpdate() {
method testCreateQueryObservesDelete (line 116) | public void testCreateQueryObservesDelete() {
method testUnsubscribeDoesNotTrigger (line 125) | public void testUnsubscribeDoesNotTrigger() {
method testQueryNotNotifiedWhenQueryTransformerDisposed (line 135) | public void testQueryNotNotifiedWhenQueryTransformerDisposed() {
method testInitialValueAndTriggerUsesScheduler (line 146) | public void testInitialValueAndTriggerUsesScheduler() {
method values (line 160) | private ContentValues values(String key, String value) {
class TestContentProvider (line 167) | public static final class TestContentProvider extends MockContentProvi...
method init (line 172) | void init(ContentResolver contentResolver) {
method insert (line 176) | @Override public Uri insert(Uri uri, ContentValues values) {
method update (line 182) | @Override public int update(Uri uri, ContentValues values, String se...
method delete (line 191) | @Override public int delete(Uri uri, String selection, String[] sele...
method query (line 198) | @Override public Cursor query(Uri uri, String[] projection, String s...
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BriteDatabaseTest.java
class BriteDatabaseTest (line 75) | @RunWith(AndroidJUnit4.class) //
method setUp (line 88) | @Before public void setUp() throws IOException {
method tearDown (line 112) | @After public void tearDown() {
method loggerEnabled (line 116) | @Test public void loggerEnabled() {
method loggerDisabled (line 122) | @Test public void loggerDisabled() {
method loggerIndentsSqlForCreateQuery (line 128) | @Test public void loggerIndentsSqlForCreateQuery() {
method loggerIndentsSqlForQuery (line 143) | @Test public void loggerIndentsSqlForQuery() {
method loggerIndentsSqlForExecute (line 153) | @Test public void loggerIndentsSqlForExecute() {
method loggerIndentsSqlForExecuteWithArgs (line 162) | @Test public void loggerIndentsSqlForExecuteWithArgs() {
method closePropagates (line 172) | @Test public void closePropagates() {
method query (line 177) | @Test public void query() {
method queryWithQueryObject (line 186) | @Test public void queryWithQueryObject() {
method queryMapToList (line 195) | @Test public void queryMapToList() {
method queryMapToOne (line 205) | @Test public void queryMapToOne() {
method queryMapToOneOrDefault (line 212) | @Test public void queryMapToOneOrDefault() {
method badQueryCallsError (line 219) | @Test public void badQueryCallsError() {
method queryWithArgs (line 226) | @Test public void queryWithArgs() {
method queryObservesInsert (line 235) | @Test public void queryObservesInsert() {
method queryInitialValueAndTriggerUsesScheduler (line 252) | @Test public void queryInitialValueAndTriggerUsesScheduler() {
method queryNotNotifiedWhenInsertFails (line 275) | @Test public void queryNotNotifiedWhenInsertFails() {
method queryNotNotifiedWhenQueryTransformerUnsubscribes (line 287) | @Test public void queryNotNotifiedWhenQueryTransformerUnsubscribes() {
method queryObservesUpdate (line 302) | @Test public void queryObservesUpdate() {
method queryNotNotifiedWhenUpdateAffectsZeroRows (line 320) | @Test public void queryNotNotifiedWhenUpdateAffectsZeroRows() {
method queryObservesDelete (line 334) | @Test public void queryObservesDelete() {
method queryNotNotifiedWhenDeleteAffectsZeroRows (line 349) | @Test public void queryNotNotifiedWhenDeleteAffectsZeroRows() {
method queryMultipleTables (line 361) | @Test public void queryMultipleTables() {
method queryMultipleTablesWithQueryObject (line 368) | @Test public void queryMultipleTablesWithQueryObject() {
method queryMultipleTablesObservesChanges (line 375) | @Test public void queryMultipleTablesObservesChanges() {
method queryMultipleTablesObservesChangesOnlyOnce (line 395) | @Test public void queryMultipleTablesObservesChangesOnlyOnce() {
method queryNotNotifiedAfterDispose (line 411) | @Test public void queryNotNotifiedAfterDispose() {
method queryOnlyNotifiedAfterSubscribe (line 424) | @Test public void queryOnlyNotifiedAfterSubscribe() {
method executeSqlNoTrigger (line 440) | @Test public void executeSqlNoTrigger() {
method executeSqlWithArgsNoTrigger (line 449) | @Test public void executeSqlWithArgsNoTrigger() {
method executeSqlAndTrigger (line 458) | @Test public void executeSqlAndTrigger() {
method executeSqlAndTriggerMultipleTables (line 475) | @Test public void executeSqlAndTriggerMultipleTables() {
method executeSqlAndTriggerWithNoTables (line 502) | @Test public void executeSqlAndTriggerWithNoTables() {
method executeSqlThrowsAndDoesNotTrigger (line 514) | @Test public void executeSqlThrowsAndDoesNotTrigger() {
method executeSqlWithArgsAndTrigger (line 528) | @Test public void executeSqlWithArgsAndTrigger() {
method executeSqlWithArgsThrowsAndDoesNotTrigger (line 545) | @Test public void executeSqlWithArgsThrowsAndDoesNotTrigger() {
method executeSqlWithArgsAndTriggerWithMultipleTables (line 559) | @Test public void executeSqlWithArgsAndTriggerWithMultipleTables() {
method executeSqlWithArgsAndTriggerWithNoTables (line 586) | @Test public void executeSqlWithArgsAndTriggerWithNoTables() {
method executeInsertAndTrigger (line 598) | @Test public void executeInsertAndTrigger() {
method executeInsertAndDontTrigger (line 619) | @Test public void executeInsertAndDontTrigger() {
method executeInsertAndTriggerMultipleTables (line 635) | @Test public void executeInsertAndTriggerMultipleTables() {
method executeInsertAndTriggerNoTables (line 668) | @Test public void executeInsertAndTriggerNoTables() {
method executeInsertThrowsAndDoesNotTrigger (line 685) | @Test public void executeInsertThrowsAndDoesNotTrigger() {
method executeInsertWithArgsAndTrigger (line 702) | @Test public void executeInsertWithArgsAndTrigger() {
method executeInsertWithArgsThrowsAndDoesNotTrigger (line 724) | @Test public void executeInsertWithArgsThrowsAndDoesNotTrigger() {
method executeUpdateDeleteAndTrigger (line 742) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
method executeUpdateDeleteAndDontTrigger (line 763) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
method executeUpdateDeleteAndTriggerWithMultipleTables (line 782) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
method executeUpdateDeleteAndTriggerWithNoTables (line 815) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
method executeUpdateDeleteThrowsAndDoesNotTrigger (line 833) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
method executeUpdateDeleteWithArgsAndTrigger (line 851) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
method executeUpdateDeleteWithArgsThrowsAndDoesNotTrigger (line 873) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
method transactionOnlyNotifiesOnce (line 892) | @Test public void transactionOnlyNotifiesOnce() {
method transactionCreatedFromTransactionNotificationWorks (line 920) | @Test public void transactionCreatedFromTransactionNotificationWorks() {
method transactionIsCloseable (line 941) | @Test public void transactionIsCloseable() throws IOException {
method transactionDoesNotThrow (line 969) | @Test public void transactionDoesNotThrow() {
method queryCreatedDuringTransactionThrows (line 995) | @Test public void queryCreatedDuringTransactionThrows() {
method querySubscribedToDuringTransactionThrows (line 1007) | @Test public void querySubscribedToDuringTransactionThrows() {
method callingEndMultipleTimesThrows (line 1015) | @Test public void callingEndMultipleTimesThrows() {
method querySubscribedToDuringTransactionOnDifferentThread (line 1026) | @Test public void querySubscribedToDuringTransactionOnDifferentThread()
method queryCreatedBeforeTransactionButSubscribedAfter (line 1051) | @Test public void queryCreatedBeforeTransactionButSubscribedAfter() {
method synchronousQueryDuringTransaction (line 1073) | @Test public void synchronousQueryDuringTransaction() {
method synchronousQueryDuringTransactionSeesChanges (line 1087) | @Test public void synchronousQueryDuringTransactionSeesChanges() {
method synchronousQueryWithSupportSQLiteQueryDuringTransaction (line 1113) | @Test public void synchronousQueryWithSupportSQLiteQueryDuringTransact...
method synchronousQueryWithSupportSQLiteQueryDuringTransactionSeesChanges (line 1127) | @Test public void synchronousQueryWithSupportSQLiteQueryDuringTransact...
method nestedTransactionsOnlyNotifyOnce (line 1153) | @Test public void nestedTransactionsOnlyNotifyOnce() {
method nestedTransactionsOnMultipleTables (line 1187) | @Test public void nestedTransactionsOnMultipleTables() {
method emptyTransactionDoesNotNotify (line 1223) | @Test public void emptyTransactionDoesNotNotify() {
method transactionRollbackDoesNotNotify (line 1240) | @Test public void transactionRollbackDoesNotNotify() {
method nonExclusiveTransactionWorks (line 1259) | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
method badQueryThrows (line 1294) | @Test public void badQueryThrows() {
method badInsertThrows (line 1304) | @Test public void badInsertThrows() {
method badUpdateThrows (line 1313) | @Test public void badUpdateThrows() {
method badDeleteThrows (line 1322) | @Test public void badDeleteThrows() {
method assertCursor (line 1331) | private static CursorAssert assertCursor(Cursor cursor) {
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/QueryObservableTest.java
class QueryObservableTest (line 26) | public final class QueryObservableTest {
method mapToListThrowsFromQueryRun (line 27) | @Test public void mapToListThrowsFromQueryRun() {
method mapToListThrowsFromMapFunction (line 45) | @Test public void mapToListThrowsFromMapFunction() {
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/QueryTest.java
class QueryTest (line 44) | public final class QueryTest {
method setUp (line 47) | @Before public void setUp() {
method mapToOne (line 59) | @Test public void mapToOne() {
method mapToOneThrowsWhenMapperReturnsNull (line 66) | @Test public void mapToOneThrowsWhenMapperReturnsNull() {
method mapToOneThrowsOnMultipleRows (line 78) | @Test public void mapToOneThrowsOnMultipleRows() {
method mapToOneIgnoresNullCursor (line 90) | @Test public void mapToOneIgnoresNullCursor() {
method mapToOneOrDefault (line 106) | @Test public void mapToOneOrDefault() {
method mapToOneOrDefaultDisallowsNullDefault (line 114) | @Test public void mapToOneOrDefaultDisallowsNullDefault() {
method mapToOneOrDefaultThrowsWhenMapperReturnsNull (line 123) | @Test public void mapToOneOrDefaultThrowsWhenMapperReturnsNull() {
method mapToOneOrDefaultThrowsOnMultipleRows (line 135) | @Test public void mapToOneOrDefaultThrowsOnMultipleRows() {
method mapToOneOrDefaultReturnsDefaultWhenNullCursor (line 148) | @Test public void mapToOneOrDefaultReturnsDefaultWhenNullCursor() {
method mapToList (line 165) | @Test public void mapToList() {
method mapToListEmptyWhenNoRows (line 175) | @Test public void mapToListEmptyWhenNoRows() {
method mapToListReturnsNullOnMapperNull (line 182) | @Test public void mapToListReturnsNullOnMapperNull() {
method mapToListIgnoresNullCursor (line 200) | @Test public void mapToListIgnoresNullCursor() {
method mapToOptional (line 216) | @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
method mapToOptionalThrowsWhenMapperReturnsNull (line 224) | @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
method mapToOptionalThrowsOnMultipleRows (line 237) | @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
method mapToOptionalIgnoresNullCursor (line 246) | @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N)
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/RecordingObserver.java
class RecordingObserver (line 27) | class RecordingObserver extends DisposableObserver<Query> {
method onComplete (line 33) | @Override public final void onComplete() {
method onError (line 38) | @Override public final void onError(Throwable e) {
method onNext (line 43) | @Override public final void onNext(Query value) {
method takeEvent (line 48) | protected Object takeEvent() {
method assertCursor (line 56) | public final CursorAssert assertCursor() {
method assertErrorContains (line 62) | public final void assertErrorContains(String expected) {
method assertIsCompleted (line 68) | public final void assertIsCompleted() {
method assertNoMoreEvents (line 73) | public void assertNoMoreEvents() {
class CursorAssert (line 77) | static final class CursorAssert {
method CursorAssert (line 81) | CursorAssert(Cursor cursor) {
method hasRow (line 85) | public CursorAssert hasRow(Object... values) {
method isExhausted (line 97) | public void isExhausted() {
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/SqlBriteTest.java
class SqlBriteTest (line 17) | @RunWith(AndroidJUnit4.class)
method builderDisallowsNull (line 24) | @Test public void builderDisallowsNull() {
method asRowsEmpty (line 40) | @Test public void asRowsEmpty() {
method asRows (line 47) | @Test public void asRows() {
method asRowsStopsWhenUnsubscribed (line 57) | @Test public void asRowsStopsWhenUnsubscribed() {
method asRowsEmptyWhenNullCursor (line 73) | @Test public void asRowsEmptyWhenNullCursor() {
class Name (line 91) | static final class Name {
method apply (line 93) | @Override public Name apply(Cursor cursor) {
method Name (line 103) | Name(String first, String last) {
method equals (line 108) | @Override public boolean equals(Object o) {
method hashCode (line 115) | @Override public int hashCode() {
method toString (line 119) | @Override public String toString() {
class CursorQuery (line 124) | static final class CursorQuery extends Query {
method CursorQuery (line 127) | CursorQuery(Cursor cursor) {
method run (line 131) | @Override public Cursor run() {
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/TestDb.java
class TestDb (line 34) | final class TestDb extends SupportSQLiteOpenHelper.Callback {
type EmployeeTable (line 50) | interface EmployeeTable {
class Employee (line 56) | static final class Employee {
method apply (line 58) | @Override public Employee apply(Cursor cursor) {
method Employee (line 68) | Employee(String username, String name) {
method equals (line 73) | @Override public boolean equals(Object o) {
method hashCode (line 80) | @Override public int hashCode() {
method toString (line 84) | @Override public String toString() {
type ManagerTable (line 89) | interface ManagerTable {
method TestDb (line 108) | TestDb() {
method onCreate (line 112) | @Override public void onCreate(@NonNull SupportSQLiteDatabase db) {
method employee (line 124) | static ContentValues employee(String username, String name) {
method manager (line 131) | static ContentValues manager(long employeeId, long managerId) {
method onUpgrade (line 138) | @Override
FILE: sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/TestScheduler.java
class TestScheduler (line 23) | final class TestScheduler extends Scheduler {
method runTasksImmediately (line 29) | public void runTasksImmediately(boolean runTasksImmediately) {
method triggerActions (line 33) | public void triggerActions() {
method createWorker (line 37) | @Override public Worker createWorker() {
class TestWorker (line 41) | class TestWorker extends Worker {
method schedule (line 44) | @Override
method dispose (line 53) | @Override public void dispose() {
method isDisposed (line 57) | @Override public boolean isDisposed() {
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/BriteContentResolver.java
class BriteContentResolver (line 45) | public final class BriteContentResolver {
method BriteContentResolver (line 55) | BriteContentResolver(ContentResolver contentResolver, Logger logger, S...
method setLoggingEnabled (line 64) | public void setLoggingEnabled(boolean enabled) {
method createQuery (line 91) | @CheckResult @NonNull
method log (line 138) | void log(String message, Object... args) {
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/BriteDatabase.java
class BriteDatabase (line 64) | public final class BriteDatabase implements Closeable {
method markSuccessful (line 74) | @Override public void markSuccessful() {
method yieldIfContendedSafely (line 79) | @Override public boolean yieldIfContendedSafely() {
method yieldIfContendedSafely (line 83) | @Override public boolean yieldIfContendedSafely(long sleepAmount, Time...
method end (line 87) | @Override public void end() {
method close (line 102) | @Override public void close() {
method accept (line 107) | @Override public void accept(Object ignored) throws Exception {
method BriteDatabase (line 119) | BriteDatabase(SupportSQLiteOpenHelper helper, Logger logger, Scheduler...
method setLoggingEnabled (line 130) | public void setLoggingEnabled(boolean enabled) {
method getReadableDatabase (line 152) | @NonNull @CheckResult @WorkerThread
method getWritableDatabase (line 176) | @NonNull @CheckResult @WorkerThread
method sendTableTrigger (line 181) | void sendTableTrigger(Set<String> tables) {
method newTransaction (line 228) | @CheckResult @NonNull
method newNonExclusiveTransaction (line 275) | @CheckResult @NonNull
method close (line 290) | @Override public void close() {
method createQuery (line 317) | @CheckResult @NonNull
method createQuery (line 329) | @CheckResult @NonNull
method createQuery (line 358) | @CheckResult @NonNull
method createQuery (line 370) | @CheckResult @NonNull
method createQuery (line 376) | @CheckResult @NonNull
method query (line 398) | @CheckResult @WorkerThread
method query (line 413) | @CheckResult @WorkerThread
method insert (line 428) | @WorkerThread
method delete (line 454) | @WorkerThread
method update (line 480) | @WorkerThread
method execute (line 510) | @WorkerThread
method execute (line 526) | @WorkerThread
method executeAndTrigger (line 542) | @WorkerThread
method executeAndTrigger (line 552) | @WorkerThread
method executeAndTrigger (line 568) | @WorkerThread
method executeAndTrigger (line 578) | @WorkerThread
method executeUpdateDelete (line 594) | @WorkerThread
method executeUpdateDelete (line 605) | @WorkerThread
method executeInsert (line 627) | @WorkerThread
method executeInsert (line 638) | @WorkerThread
type Transaction (line 651) | public interface Transaction extends Closeable {
method end (line 658) | @WorkerThread
method markSuccessful (line 669) | @WorkerThread
method yieldIfContendedSafely (line 683) | @WorkerThread
method yieldIfContendedSafely (line 700) | @WorkerThread
method close (line 706) | @WorkerThread
method indentSql (line 722) | static String indentSql(String sql) {
method log (line 726) | void log(String message, Object... args) {
method conflictString (line 731) | private static String conflictString(@ConflictAlgorithm int conflictAl...
class SqliteTransaction (line 750) | static final class SqliteTransaction extends LinkedHashSet<String>
method SqliteTransaction (line 755) | SqliteTransaction(SqliteTransaction parent) {
method onBegin (line 759) | @Override public void onBegin() {
method onCommit (line 762) | @Override public void onCommit() {
method onRollback (line 766) | @Override public void onRollback() {
method toString (line 769) | @Override public String toString() {
class DatabaseQuery (line 775) | final class DatabaseQuery extends Query
method DatabaseQuery (line 780) | DatabaseQuery(Iterable<String> tables, SupportSQLiteQuery query) {
method run (line 785) | @Override public Cursor run() {
method toString (line 799) | @Override public String toString() {
method apply (line 803) | @Override public Query apply(Set<String> ignored) {
method test (line 807) | @Override public boolean test(Set<String> strings) {
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/QueryObservable.java
class QueryObservable (line 16) | public final class QueryObservable extends Observable<Query> {
method apply (line 19) | @Override public QueryObservable apply(Observable<Query> queryObservab...
method QueryObservable (line 26) | public QueryObservable(Observable<Query> upstream) {
method subscribeActual (line 30) | @Override protected void subscribeActual(Observer<? super Query> obser...
method mapToOne (line 53) | @CheckResult @NonNull
method mapToOneOrDefault (line 78) | @CheckResult @NonNull
method mapToOptional (line 103) | @RequiresApi(Build.VERSION_CODES.N)
method mapToList (line 130) | @CheckResult @NonNull
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/QueryToListOperator.java
class QueryToListOperator (line 28) | final class QueryToListOperator<T> implements ObservableOperator<List<T>...
method QueryToListOperator (line 31) | QueryToListOperator(Function<Cursor, T> mapper) {
method apply (line 35) | @Override public Observer<? super SqlBrite.Query> apply(Observer<? sup...
class MappingObserver (line 39) | static final class MappingObserver<T> extends DisposableObserver<SqlBr...
method MappingObserver (line 43) | MappingObserver(Observer<? super List<T>> downstream, Function<Curso...
method onStart (line 48) | @Override protected void onStart() {
method onNext (line 52) | @Override public void onNext(SqlBrite.Query query) {
method onComplete (line 75) | @Override public void onComplete() {
method onError (line 81) | @Override public void onError(Throwable e) {
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/QueryToOneOperator.java
class QueryToOneOperator (line 27) | final class QueryToOneOperator<T> implements ObservableOperator<T, SqlBr...
method QueryToOneOperator (line 32) | QueryToOneOperator(Function<Cursor, T> mapper, @Nullable T defaultValu...
method apply (line 37) | @Override public Observer<? super SqlBrite.Query> apply(Observer<? sup...
class MappingObserver (line 41) | static final class MappingObserver<T> extends DisposableObserver<SqlBr...
method MappingObserver (line 46) | MappingObserver(Observer<? super T> downstream, Function<Cursor, T> ...
method onStart (line 52) | @Override protected void onStart() {
method onNext (line 56) | @Override public void onNext(SqlBrite.Query query) {
method onComplete (line 89) | @Override public void onComplete() {
method onError (line 95) | @Override public void onError(Throwable e) {
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/QueryToOptionalOperator.java
class QueryToOptionalOperator (line 29) | @RequiresApi(Build.VERSION_CODES.N)
method QueryToOptionalOperator (line 33) | QueryToOptionalOperator(Function<Cursor, T> mapper) {
method apply (line 37) | @Override public Observer<? super SqlBrite.Query> apply(Observer<? sup...
class MappingObserver (line 41) | static final class MappingObserver<T> extends DisposableObserver<SqlBr...
method MappingObserver (line 45) | MappingObserver(Observer<? super Optional<T>> downstream, Function<C...
method onStart (line 50) | @Override protected void onStart() {
method onNext (line 54) | @Override public void onNext(SqlBrite.Query query) {
method onComplete (line 83) | @Override public void onComplete() {
method onError (line 89) | @Override public void onError(Throwable e) {
FILE: sqlbrite/src/main/java/com/squareup/sqlbrite3/SqlBrite.java
class SqlBrite (line 42) | public final class SqlBrite {
method log (line 44) | @Override public void log(String message) {
method apply (line 50) | @Override public Observable<Query> apply(Observable<Query> queryObserv...
class Builder (line 55) | public static final class Builder {
method logger (line 59) | @CheckResult
method queryTransformer (line 66) | @CheckResult
method build (line 73) | @CheckResult
method SqlBrite (line 82) | SqlBrite(@NonNull Logger logger, @NonNull ObservableTransformer<Query,...
method wrapDatabaseHelper (line 98) | @CheckResult @NonNull public BriteDatabase wrapDatabaseHelper(
method wrapContentProvider (line 110) | @CheckResult @NonNull public BriteContentResolver wrapContentProvider(
class Query (line 116) | public static abstract class Query {
method mapToOne (line 129) | @CheckResult @NonNull //
method mapToOneOrDefault (line 147) | @SuppressWarnings("ConstantConditions") // Public API contract.
method mapToOptional (line 167) | @RequiresApi(Build.VERSION_CODES.N) //
method mapToList (line 186) | @CheckResult @NonNull
method run (line 201) | @CheckResult @WorkerThread
method asRows (line 227) | @CheckResult @NonNull
type Logger (line 250) | public interface Logger {
method log (line 251) | void log(String message);
Condensed preview — 70 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (267K chars).
[
{
"path": ".buildscript/deploy_snapshot.sh",
"chars": 937,
"preview": "#!/bin/bash\n#\n# Deploy a jar, source jar, and javadoc jar to Sonatype's snapshot repo.\n#\n# Adapted from https://coderwal"
},
{
"path": ".gitignore",
"chars": 108,
"preview": "# IntelliJ IDEA\n.idea\n*.iml\n\n# Gradle\n.gradle\ngradlew.bat\nbuild\nlocal.properties\nreports\n\n# Apple\n.DS_Store\n"
},
{
"path": ".travis.yml",
"chars": 1500,
"preview": "language: android\n\nandroid:\n components:\n - tools\n - platform-tools\n\njdk:\n - oraclejdk8\n\nbefore_install:\n # Ins"
},
{
"path": "CHANGELOG.md",
"chars": 9624,
"preview": "Change Log\n==========\n\nVersion 3.2.0 *(2018-03-05)*\n----------------------------\n\n * New: Add `query(SupportSQLiteQuery)"
},
{
"path": "CONTRIBUTING.md",
"chars": 603,
"preview": "Contributing\n============\n\nIf you would like to contribute code you can do so through GitHub by forking\nthe repository a"
},
{
"path": "LICENSE.txt",
"chars": 11358,
"preview": "\n Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 6517,
"preview": "SQL Brite\n=========\n\nA lightweight wrapper around `SupportSQLiteOpenHelper` and `ContentResolver` which introduces react"
},
{
"path": "RELEASING.md",
"chars": 731,
"preview": "Releasing\n========\n\n 1. Change the version in `gradle.properties` to a non-SNAPSHOT version.\n 2. Update the `CHANGELOG.m"
},
{
"path": "build.gradle",
"chars": 2690,
"preview": "buildscript {\n ext.versions = [\n 'minSdk': 14,\n 'compileSdk': 27,\n 'kotlin': '1.1.60',\n 'lint': '26"
},
{
"path": "gradle/gradle-mvn-push.gradle",
"chars": 3574,
"preview": "/*\n * Copyright 2013 Chris Banes\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not us"
},
{
"path": "gradle/wrapper/gradle-wrapper.properties",
"chars": 200,
"preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dist"
},
{
"path": "gradle.properties",
"chars": 610,
"preview": "GROUP=com.squareup.sqlbrite3\nVERSION_NAME=3.2.1-SNAPSHOT\n\nPOM_DESCRIPTION=A lightweight wrapper around SQLiteOpenHelper "
},
{
"path": "gradlew",
"chars": 5296,
"preview": "#!/usr/bin/env sh\n\n##############################################################################\n##\n## Gradle start up"
},
{
"path": "sample/build.gradle",
"chars": 1570,
"preview": "apply plugin: 'com.android.application'\n\ndependencies {\n implementation rootProject.ext.supportV4\n implementation root"
},
{
"path": "sample/src/main/AndroidManifest.xml",
"chars": 675,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n package="
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/TodoApp.java",
"chars": 1192,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/TodoComponent.java",
"chars": 1151,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/TodoModule.java",
"chars": 1094,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/db/Db.java",
"chars": 1408,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/db/DbCallback.java",
"chars": 4601,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/db/DbModule.java",
"chars": 2003,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/db/TodoItem.java",
"chars": 2377,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/db/TodoList.java",
"chars": 1683,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/ui/ItemsAdapter.java",
"chars": 2457,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/ui/ItemsFragment.java",
"chars": 7267,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/ui/ListsAdapter.java",
"chars": 1999,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/ui/ListsFragment.java",
"chars": 3817,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/ui/ListsItem.java",
"chars": 2310,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/ui/MainActivity.java",
"chars": 1804,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/ui/NewItemFragment.java",
"chars": 3679,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/java/com/example/sqlbrite/todo/ui/NewListFragment.java",
"chars": 3312,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sample/src/main/res/anim/slide_in_left.xml",
"chars": 1044,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n/* //device/apps/common/res/anim/slide_in_left.xml\n**\n** Copyright 2007, The"
},
{
"path": "sample/src/main/res/anim/slide_in_right.xml",
"chars": 1044,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n/* //device/apps/common/res/anim/slide_in_right.xml\n**\n** Copyright 2007, Th"
},
{
"path": "sample/src/main/res/anim/slide_out_left.xml",
"chars": 1045,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n/* //device/apps/common/res/anim/slide_out_left.xml\n**\n** Copyright 2007, Th"
},
{
"path": "sample/src/main/res/anim/slide_out_right.xml",
"chars": 1045,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n/* //device/apps/common/res/anim/slide_out_right.xml\n**\n** Copyright 2007, T"
},
{
"path": "sample/src/main/res/layout/items.xml",
"chars": 585,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "sample/src/main/res/layout/lists.xml",
"chars": 585,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "sample/src/main/res/layout/new_item.xml",
"chars": 490,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "sample/src/main/res/layout/new_list.xml",
"chars": 486,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<FrameLayout\n xmlns:android=\"http://schemas.android.com/apk/res/android\"\n "
},
{
"path": "sample/src/main/res/values/strings.xml",
"chars": 380,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n <string name=\"app_name\">SqlBrite To-Do</string>\n\n <string name=\"cr"
},
{
"path": "settings.gradle",
"chars": 126,
"preview": "include ':sqlbrite'\ninclude ':sqlbrite-kotlin'\ninclude ':sqlbrite-lint'\ninclude ':sample'\n\nrootProject.name = 'sqlbrite-"
},
{
"path": "sqlbrite/build.gradle",
"chars": 987,
"preview": "apply plugin: 'com.android.library'\n\ndependencies {\n api rootProject.ext.rxJava\n api rootProject.ext.supportSqlite\n i"
},
{
"path": "sqlbrite/gradle.properties",
"chars": 61,
"preview": "POM_ARTIFACT_ID=sqlbrite\nPOM_NAME=SqlBrite\nPOM_PACKAGING=aar\n"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BlockingRecordingObserver.java",
"chars": 1292,
"preview": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BriteContentResolverTest.java",
"chars": 7098,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/BriteDatabaseTest.java",
"chars": 45259,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/QueryObservableTest.java",
"chars": 2171,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/QueryTest.java",
"chars": 8983,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/RecordingObserver.java",
"chars": 3390,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/SqlBriteTest.java",
"chars": 4119,
"preview": "package com.squareup.sqlbrite3;\n\nimport android.database.Cursor;\nimport android.database.MatrixCursor;\nimport android.su"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/TestDb.java",
"chars": 5216,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/androidTest/java/com/squareup/sqlbrite3/TestScheduler.java",
"chars": 1824,
"preview": "/*\n * Copyright (C) 2016 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/main/AndroidManifest.xml",
"chars": 45,
"preview": "<manifest package=\"com.squareup.sqlbrite2\"/>\n"
},
{
"path": "sqlbrite/src/main/java/com/squareup/sqlbrite3/BriteContentResolver.java",
"chars": 5861,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/main/java/com/squareup/sqlbrite3/BriteDatabase.java",
"chars": 30471,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/main/java/com/squareup/sqlbrite3/QueryObservable.java",
"chars": 5101,
"preview": "package com.squareup.sqlbrite3;\n\nimport android.database.Cursor;\nimport android.os.Build;\nimport android.support.annotat"
},
{
"path": "sqlbrite/src/main/java/com/squareup/sqlbrite3/QueryToListOperator.java",
"chars": 2642,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/main/java/com/squareup/sqlbrite3/QueryToOneOperator.java",
"chars": 3269,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/main/java/com/squareup/sqlbrite3/QueryToOptionalOperator.java",
"chars": 3018,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite/src/main/java/com/squareup/sqlbrite3/SqlBrite.java",
"chars": 10383,
"preview": "/*\n * Copyright (C) 2015 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite-kotlin/build.gradle",
"chars": 821,
"preview": "apply plugin: 'com.android.library'\napply plugin: 'org.jetbrains.kotlin.android'\n\ndependencies {\n api project(':sqlbrit"
},
{
"path": "sqlbrite-kotlin/gradle.properties",
"chars": 88,
"preview": "POM_ARTIFACT_ID=sqlbrite-kotlin\nPOM_NAME=SqlBrite (Kotlin Extensions)\nPOM_PACKAGING=aar\n"
},
{
"path": "sqlbrite-kotlin/src/main/AndroidManifest.xml",
"chars": 52,
"preview": "<manifest package=\"com.squareup.sqlbrite2.kotlin\"/>\n"
},
{
"path": "sqlbrite-kotlin/src/main/java/com/squareup/sqlbrite3/extensions.kt",
"chars": 4059,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite-lint/build.gradle",
"chars": 361,
"preview": "apply plugin: 'kotlin'\n\ndependencies {\n compileOnly rootProject.ext.kotlinStdLib\n compileOnly rootProject.ext.lintApi\n"
},
{
"path": "sqlbrite-lint/src/main/java/com/squareup/sqlbrite3/BriteIssueRegistry.kt",
"chars": 805,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite-lint/src/main/java/com/squareup/sqlbrite3/SqlBriteArgCountDetector.kt",
"chars": 3322,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
},
{
"path": "sqlbrite-lint/src/test/java/com/squareup/sqlbrite3/SqlBriteArgCountDetectorTest.kt",
"chars": 5954,
"preview": "/*\n * Copyright (C) 2017 Square, Inc.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may n"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the square/sqlbrite GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 70 files (245.7 KB), approximately 60.3k tokens, and a symbol index with 390 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.