Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SQLDelight interaction example #814

Merged
merged 3 commits into from Sep 4, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle
Expand Up @@ -23,6 +23,8 @@ buildscript {
// Automatic releases to Sonatype.
classpath 'io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.8.0'

classpath 'com.squareup.sqldelight:gradle-plugin:0.6.1'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
Expand Down
4 changes: 4 additions & 0 deletions storio-sample-app/build.gradle
@@ -1,4 +1,5 @@
apply plugin: 'com.android.application'
apply plugin: 'com.squareup.sqldelight'

android {
compileSdkVersion rootProject.ext.compileSdkVersion
Expand Down Expand Up @@ -68,6 +69,9 @@ dependencies {
compile libraries.recyclerView
compile libraries.timber

provided libraries.autoParcel
annotationProcessor libraries.autoParcelProcessor

debugCompile libraries.leakCanary
releaseCompile libraries.leakCanaryNoOp

Expand Down
4 changes: 4 additions & 0 deletions storio-sample-app/src/main/AndroidManifest.xml
Expand Up @@ -30,6 +30,10 @@
android:name="com.pushtorefresh.storio2.sample.many_to_many_sample.ManyToManyActivity"
android:label="@string/many_to_many" />

<activity
android:name="com.pushtorefresh.storio2.sample.sqldelight.SqlDelightActivity"
android:label="@string/sqldelight" />

<provider
android:name="com.pushtorefresh.storio2.sample.provider.SampleContentProvider"
android:authorities="com.pushtorefresh.storio2.sample_provider"
Expand Down
Expand Up @@ -5,6 +5,7 @@
import com.pushtorefresh.storio2.sample.db.DbModule;
import com.pushtorefresh.storio2.sample.many_to_many_sample.ManyToManyActivity;
import com.pushtorefresh.storio2.sample.provider.SampleContentProvider;
import com.pushtorefresh.storio2.sample.sqldelight.SqlDelightActivity;
import com.pushtorefresh.storio2.sample.ui.fragment.TweetsFragment;
import com.pushtorefresh.storio2.sqlite.StorIOSQLite;

Expand All @@ -25,6 +26,8 @@ public interface AppComponent {

void inject(@NonNull ManyToManyActivity manyToManyActivity);

void inject(@NonNull SqlDelightActivity sqlDelightActivity);

void inject(@NonNull SampleContentProvider sampleContentProvider);

@NonNull
Expand Down
Expand Up @@ -10,6 +10,7 @@
import com.pushtorefresh.storio2.sample.many_to_many_sample.entities.CarTable;
import com.pushtorefresh.storio2.sample.many_to_many_sample.entities.PersonCarRelationTable;
import com.pushtorefresh.storio2.sample.many_to_many_sample.entities.PersonTable;
import com.pushtorefresh.storio2.sample.sqldelight.entities.Customer;

public class DbOpenHelper extends SQLiteOpenHelper {

Expand All @@ -25,6 +26,8 @@ public void onCreate(@NonNull SQLiteDatabase db) {
db.execSQL(CarTable.getCreateTableQuery());
db.execSQL(PersonTable.getCreateTableQuery());
db.execSQL(PersonCarRelationTable.getCreateTableQuery());

db.execSQL(Customer.CREATE_TABLE);
}

@Override
Expand Down
@@ -0,0 +1,62 @@
package com.pushtorefresh.storio2.sample.sqldelight;

import android.annotation.SuppressLint;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import com.pushtorefresh.storio2.sample.R;
import com.pushtorefresh.storio2.sample.sqldelight.entities.Customer;

import java.util.List;

import butterknife.Bind;
import butterknife.ButterKnife;

public class CustomersAdapter extends RecyclerView.Adapter<CustomersAdapter.ViewHolder> {

private List<Customer> customers;

public void setCustomers(@Nullable List<Customer> customers) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not allow null and just pass empty list if needed?

this.customers = customers;
notifyDataSetChanged();
}

@Override
public int getItemCount() {
return customers == null ? 0 : customers.size();
}

@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cache LayoutInflater?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would accept it as constructor parameter

.inflate(R.layout.list_item_customer, parent, false));
}

@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
final Customer customer = customers.get(position);

holder.name.setText(String.format("%s %s", customer.name(), customer.surname()));
holder.city.setText(customer.city());
}

static class ViewHolder extends RecyclerView.ViewHolder {

@Bind(R.id.list_item_customer_name)
TextView name;

@Bind(R.id.list_item_customer_city)
TextView city;

public ViewHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
@@ -0,0 +1,72 @@
package com.pushtorefresh.storio2.sample.sqldelight;

import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.NonNull;

import com.pushtorefresh.storio2.sqlite.StorIOSQLite;
import com.pushtorefresh.storio2.sqlite.operations.put.PutResolver;
import com.pushtorefresh.storio2.sqlite.operations.put.PutResult;
import com.pushtorefresh.storio2.sqlite.queries.InsertQuery;
import com.pushtorefresh.storio2.sqlite.queries.RawQuery;
import com.squareup.sqldelight.RowMapper;
import com.squareup.sqldelight.SqlDelightStatement;

import java.util.ArrayList;
import java.util.List;

import static com.pushtorefresh.storio2.sqlite.operations.put.PutResult.newInsertResult;

public final class SQLUtils {

private SQLUtils() {
}

@NonNull
public static PutResolver<ContentValues> makeSimpleContentValuesInsertPutResolver(@NonNull final String tableName) {
return new PutResolver<ContentValues>() {
@NonNull
final InsertQuery insert = InsertQuery.builder()
.table(tableName)
.build();

@NonNull
@Override
public PutResult performPut(@NonNull StorIOSQLite storIOSQLite, @NonNull ContentValues contentValues) {
final long insertedId = storIOSQLite.lowLevel().insert(insert, contentValues);
return newInsertResult(insertedId, tableName);
}
};
}

@NonNull
public static RawQuery makeReadQuery(@NonNull SqlDelightStatement statement) {
return RawQuery.builder()
.query(statement.statement)
.args(statement.args)
.observesTables(statement.tables)
.build();
}

@NonNull
public static RawQuery makeWriteQuery(@NonNull SqlDelightStatement statement) {
return RawQuery.builder()
.query(statement.statement)
.args(statement.args)
.affectsTables(statement.tables)
.build();
}

@NonNull
public static <T> List<T> mapFromCursor(@NonNull Cursor cursor, @NonNull RowMapper<? extends T> mapper) {
try {
final List<T> result = new ArrayList<T>(cursor.getCount());
while (cursor.moveToNext()) {
result.add(mapper.map(cursor));
}
return result;
} finally {
cursor.close();
}
}
}
@@ -0,0 +1,151 @@
package com.pushtorefresh.storio2.sample.sqldelight;

import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import com.pushtorefresh.storio2.sample.R;
import com.pushtorefresh.storio2.sample.SampleApp;
import com.pushtorefresh.storio2.sample.sqldelight.entities.Customer;
import com.pushtorefresh.storio2.sample.ui.UiStateController;
import com.pushtorefresh.storio2.sample.ui.activity.BaseActivity;
import com.pushtorefresh.storio2.sqlite.StorIOSQLite;

import java.util.ArrayList;
import java.util.List;

import javax.inject.Inject;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.functions.Func1;
import timber.log.Timber;

import static com.pushtorefresh.storio2.sample.sqldelight.SQLUtils.makeReadQuery;
import static com.pushtorefresh.storio2.sample.sqldelight.SQLUtils.mapFromCursor;
import static com.pushtorefresh.storio2.sample.ui.Toasts.safeShowShortToast;
import static java.util.concurrent.TimeUnit.SECONDS;
import static rx.android.schedulers.AndroidSchedulers.mainThread;

public class SqlDelightActivity extends BaseActivity {

@Inject
StorIOSQLite storIOSQLite;

UiStateController uiStateController;

@Bind(R.id.customers_recycler_view)
RecyclerView recyclerView;

CustomersAdapter customersAdapter;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqldelight);
SampleApp.get(this).appComponent().inject(this);
customersAdapter = new CustomersAdapter();

ButterKnife.bind(this);

recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(customersAdapter);
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));

uiStateController = new UiStateController.Builder()
.withLoadingUi(findViewById(R.id.customer_loading_ui))
.withErrorUi(findViewById(R.id.customers_error_ui))
.withEmptyUi(findViewById(R.id.customers_empty_ui))
.withContentUi(recyclerView)
.build();
}

@Override
public void onStart() {
super.onStart();
loadData();
}

void loadData() {
uiStateController.setUiStateLoading();

final Subscription subscription = storIOSQLite
.get()
.cursor()
.withQuery(makeReadQuery(Customer.FACTORY.select_all()))
.prepare()
.asRxObservable()
.map(new Func1<Cursor, List<Customer>>() {
@Override
public List<Customer> call(Cursor cursor) {
return mapFromCursor(cursor, Customer.CURSOR_MAPPER);
}
})
.delay(1, SECONDS)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

.observeOn(mainThread())
.subscribe(new Action1<List<Customer>>() {
@Override
public void call(List<Customer> customers) {
if (customers.isEmpty()) {
uiStateController.setUiStateEmpty();
customersAdapter.setCustomers(null);
} else {
uiStateController.setUiStateContent();
customersAdapter.setCustomers(customers);
}
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
Timber.e(throwable, "loadData()");
uiStateController.setUiStateError();
customersAdapter.setCustomers(null);
}
});
unsubscribeOnStop(subscription);
}

@OnClick(R.id.customers_empty_ui_add_button)
void addContent() {
final List<Customer> customers = new ArrayList<Customer>();

customers.add(Customer.builder().name("Artem").surname("Zinnatullin").city("SF").build());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loool

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove that though :D

customers.add(Customer.builder().name("Elon").surname("Musk").city("Boring").build());
customers.add(Customer.builder().name("Jake").surname("Wharton").city("Pittsburgh").build());

List<ContentValues> contentValues = new ArrayList<ContentValues>(customers.size());
for (Customer customer : customers) {
contentValues.add(Customer.FACTORY.marshal(customer).asContentValues());
}

storIOSQLite
.put()
.contentValues(contentValues)
.withPutResolver(Customer.CV_PUT_RESOLVER)
.prepare()
.asRxCompletable()
.observeOn(mainThread())
.subscribe(
new Action0() {
@Override
public void call() {
// no impl required
}
},
new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
safeShowShortToast(SqlDelightActivity.this, R.string.common_error);
}
});
}
}