• Stars
    star
    195
  • Rank 199,374 (Top 4 %)
  • Language
    Java
  • License
    MIT License
  • Created over 10 years ago
  • Updated over 7 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A library that encapsulates the repeatable actions of Android Cursors.

cursor_utils_logo

Build Status

Working with Android SQLite Cursors is less than ideal. Code can easily be duplicated throughout your codebase if you're not careful to keep the translation of SQLite columns into Java objects. Iterating across the returned data from a Cursor is another area which can lead to code duplication. Sometimes you actually want a Cursor to be a List, yet other times a List to be a Cursor.

This library was designed to encapsulate the repeatable actions for Android Cursors and treat them as if they were closer to a Java object rather than a conglomerate of dicts/hashes.

Here is a presentation from the NYC Android Meetup on August 27, 2014, about the library on SlideShare.

IterableCursor (and IterableCursorWrapper)

At minimum, you should use an IterableCursorWrapper and never look at a plain old Android Cursor again.

public class User {
    public User(String name, String bio) { /* ... */ }
}

public class UserCursor extends IterableCursorWrapper<User> {

    public UserCursor(Cursor c) {
        super(c);
    }

    public User peek() {
        String name = getString(COLUMN_USER_NAME, "Default Name");
        String bio = getLong(COLUMN_USER_BIO, "No bio yet");
        return new User(name, bio);
    }
    
}

public class MyDatabase extends SQLiteOpenHelper {

    // ...
    
    public IterableCursor<User> queryAllUsers() {
        Cursor cursor = getReadableDatabase().query(TABLE_USERS, null, null, null, null, null,
                null, null);
        return new UserCursor(cursor);
    }
    
}

VoilΓ ! Now you can use an enhanced for loop to access all Users at once:

IterableCursor<User> users = myDb.queryAllUsers();
for (User user : users) {
    if (user.isMyBestFriend()) giveSomeCake(user);
}
users.close();

This also simplifies using SQL-backed CursorAdapters (via IterableCursorAdapter<T>:

@Override
public void bindView(View v, Context context, User user) {
    ViewHolder holder = v.getTag();
    holder.textView.setText(user.getFullName());
}

IterableCursorAdapter is a combination of the newView() / bindView() API of CursorAdapter, with the direct item access of ArrayAdapter<T>.

CursorList

Often, certain queries are difficult to express in SQL constraints and it's much simpler to defer to Java to do filtering. But what happens when you're done filtering: you're left with a List but you want to use your favorite CursorAdapter. To solve this issue, mask your List as a CursorList:

IterableCursor<User> users = myDb.queryAllUsers();
List<User> bestFriends = new LinkedList<User>();
for (User user : users) {
    if (user.isMyBestFriend()) bestFriends.add(user);
}

IterableCursor<User> bestFriendCursor = new CursorList<User>(bestFriends);
bestFriendsListView.setAdapter(new UserCursorAdapter(bestFriendsCursor));

CursorList is an instance of both android.database.Cursor and java.util.List; it's the best of both worlds.

IterableCursor + IterableCursor

What if you wanted to have one master ListView that prioritized best friends first, but then listed everyone alphabetically?

List<User> bestFriendsList = computeBestFriendsFromCursor(); // see above
IterableCursor<User> bestFriends = new CursorList(bestFriendsList);
IterableCursor<User> allUsers = myDb.queryAllUsers();

IterableCursor<User> mergedCursor = new IterableMergeCursor<User>(bestFriends, allUsers);
listView.setAdapter(new UserCursorAdapter(mergedCursor));

The adapter has no idea that it's getting two cursors - it still gets to peek() for a User and doesn't need to know that you already did some sorting.

Cursor helper methods

Getting data at a particular column of a Cursor is a pain. And what happens if you have a custom query that only returns certain columns and not the full row?

String field1 = cursor.getString(cursor.getColumnIndex(COLUMN_FIELD1));
int field2 = cursor.getInt(cursor.getColumnIndex(COLUMN_FIELD2));
try {
    boolean field3 = cursor.getBoolean(cursor.getColumnIndex(COLUMN_FIELD3));
} catch (Exception) {
    // oh no, field3 wasn't returned this time! what should I do???
}
// ...

IterableCursorWrapper provides convenience methods which remove this headache with simpler methods and default values.

public User peek() {
    String field1 = getString(COLUMN_FIELD1, "default");
    int field2 = getInteger(COLUMN_FIELD2, 0);
    boolean field3 = getBoolean(COLUMN_FIELD3, DEFAULT_FIELD3_VALUE);
}

Cursor β†’ Collection

If the cursor size isn't too big and you just want to deal with a Collection instead, CursorUtils has some methods to translate the IterableCursor into your favorite collection:

IterableCursor<User> cursor1 = queryAllUsers();
ArrayList<User> all = CursorUtils.consumeToArrayList(cursor1);
// also comes with consumeToLinkedList()

IterableCursor<Tweet> cursor2 = queryAllTweetsAndRetweets();
LinkedHashSet<User> noRepeats = CursorUtils.consumeToLinkedHashSet(cursor2);

IterableCursor<GooglePlusUser> cursor3 = queryComplicatedDataSet();
CoolGuavaCollection<GooglePlusUser> hashMultiQueue = Guava.newAwesome();
CursorUtils.consumeToCollection(hashMultiQueue, cursor3);

Download

Gradle:

compile 'com.venmo.cursor:library:0.4'

Maven:

<dependency>
  <groupId>com.venmo.cursor>
  <artifactId>library</artifactId>
  <version>0.4</version>
</dependency>

Jar: [direct download link]

Contributing

We'd love to see your ideas for improving this library! The best way to contribute is by submitting a pull request – we'll do our best to respond to your patch as soon as possible. You can also submit an issue if you find bugs or have any questions. :octocat:

Please make sure to follow our general coding style and add test coverage for new features!

Contributors

@tpoulos for an awesome logo!

More Repositories

1

synx

A command-line tool that reorganizes your Xcode project folder to match your Xcode groups
Ruby
6,082
star
2

Static

Simple static table views for iOS in Swift.
Swift
1,250
star
3

VENTouchLock

A Touch ID and Passcode framework used in the Venmo app.
Objective-C
965
star
4

business-rules

Python DSL for setting up business intelligence rules that can be configured without code
Python
891
star
5

VENTokenField

Easy-to-use token field that is used in the Venmo app.
Objective-C
793
star
6

VENCalculatorInputView

Calculator keyboard used in the Venmo iOS app
Objective-C
763
star
7

DVR

Network testing for Swift
Swift
651
star
8

tooltip-view

Dead simple Android Tooltip Views
Java
486
star
9

DryDock-iOS

DEPRECATED: An open-source internal installer app
433
star
10

VENSeparatorView

Jagged border separators on UIViews used in the Venmo app.
Objective-C
379
star
11

VENVersionTracker

Objective-C
300
star
12

business-rules-ui

A JavaScript library for building out the logic and UI for business rules.
JavaScript
197
star
13

VENPromotionsManager

iOS Library to perform location & time-based promotions.
Objective-C
197
star
14

venmo-ios-sdk

Make and accept payments in your iOS app via Venmo
Objective-C
176
star
15

react-html-document

A foundational React component useful for rendering full html documents on the server.
JavaScript
155
star
16

VENExperimentsManager

An Objective-C library enabling easy implementation of feature experiments on iOS allowing users to opt in and out of experiments at will.
Objective-C
76
star
17

slouch

A lightweight Python framework for building cli-inspired Slack bots.
Python
71
star
18

xcode_server

Xcode Bot client
Ruby
58
star
19

android-pin

An easy drop-in PIN controller for Android
Java
42
star
20

app-switch-android

Java
35
star
21

VENCore

Core Objective-C client library for the Venmo API
Objective-C
27
star
22

QuizTrain

Swift Framework for TestRail's API
Swift
19
star
23

feature_ramp

Toggling and ramping features via a lightweight Redis backend.
Python
18
star
24

btnamespace

A Python library to isolate state on the Braintree sandbox during testing.
Python
17
star
25

flaskeleton

Python
13
star
26

swaggergenerator

Create swagger / OpenAPI schemas from example interactions.
Python
11
star
27

tornado-stub-client

A stub library for making requests with tornado's AsyncHTTPClient
Python
9
star
28

venmo.github.io

Old Venmo Engineering Blog
CSS
8
star
29

nose-detecthttp

A nose plugin that can detect tests making external http calls.
Python
7
star
30

puppet-consulr

Dynamic puppet manifests using consul
Ruby
5
star
31

puppet-sentry

Puppet module for managing the Sentry realtime event logging and aggregation platform
Ruby
4
star
32

python3-avro

Copies the python3 client implementation from our fork of apache's avro project.
Python
4
star
33

nose-seed-faker

A nose plugin that seeds the faker package
Python
4
star
34

QuizTrainExample

Example of how to use QuizTrain with Unit Tests and UI Tests on iOS
Swift
3
star
35

single-click-highlightable

HOC for React that allows users to highlight text on elements without triggering the onClick handler
JavaScript
3
star
36

puppet-hound

Puppet hound module
Puppet
2
star
37

nose-timeout

Nose plugin for aborting long running tests
Python
1
star
38

foundations-interview

This is a test repository used in Foundations team interview process
JavaScript
1
star