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

Notification storage #47

Merged
merged 6 commits into from
Aug 19, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})

def supportLibVersion = '24.2.0'
compile "com.android.support:appcompat-v7:${supportLibVersion}"
compile "com.android.support:cardview-v7:${supportLibVersion}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public Uri insert(@NonNull Uri uri, ContentValues contentValues) {

@Override
public int delete(@NonNull Uri uri, String s, String[] strings) {
return 0;
throw new UnsupportedOperationException();
}

@Override
Expand Down
111 changes: 71 additions & 40 deletions app/src/main/java/com/g11x/checklistapp/NotificationListActivity.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.g11x.checklistapp;

import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
Expand All @@ -9,10 +14,14 @@
import android.view.ViewGroup;
import android.widget.TextView;

import com.g11x.checklistapp.data.Database;
import com.g11x.checklistapp.data.Notification;

public class NotificationListActivity extends NavigationActivity {

private NotificationAdapter adapter;
private RecyclerView mRecyclerView;

@Override
protected int getNavDrawerItemIndex() {
return NAVDRAWER_ITEM_NOTIFICATIONS;
Expand All @@ -22,7 +31,7 @@ protected int getNavDrawerItemIndex() {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_notification_list);
RecyclerView mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
Expand All @@ -40,58 +49,74 @@ public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHol

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// TODO: Remove item from DB (when db exists).
long id = ((NotificationAdapter.ViewHolder) viewHolder).id;

ContentValues newValues = new ContentValues();
newValues.put(Database.Notification.READ_COLUMN, true);

getContentResolver().update(
Database.Notification.CONTENT_URI,
newValues,
Database.ID_COLUMN + " = " + id,
null
);
}
});
helper.attachToRecyclerView(mRecyclerView);

// TODO: Replace dataset with db cursor.
Notification[] dataset = new Notification[]{
new Notification("message 1", null),
new Notification("message 2", "I'm a title!"),
new Notification("message C", "I can't count :("),
new Notification("I like fish, fishy fishy fish.", null)
};
RecyclerView.Adapter mAdapter = new NotificationAdapter(dataset);
mRecyclerView.setAdapter(mAdapter);
}

public class NotificationAdapter extends RecyclerView.Adapter<NotificationAdapter.ViewHolder> {
private final Notification[] mDataset;
getSupportLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
Loader<Cursor> cursor = new CursorLoader(NotificationListActivity.this,
Database.Notification.CONTENT_URI,
Database.Notification.PROJECTION, "NOT " + Database.Notification.READ_COLUMN, null, Database.Notification.SENT_TIME);
return cursor;
}

// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public final TextView mTitle;
public final TextView mBody;
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
if (adapter == null) {
if (cursor != null) {
adapter = new NotificationAdapter(cursor);
mRecyclerView.setAdapter(adapter);
}
} else {
adapter.swapCursor(cursor);
}
}

public ViewHolder(View v) {
super(v);
mTitle = (TextView) v.findViewById(R.id.notification_title);
mBody = (TextView) v.findViewById(R.id.notification_body);
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
});
}

@Override
protected void onStart() {
super.onStart();
if (adapter != null) {
adapter.notifyDataSetChanged();
}
}

private static class NotificationAdapter extends RecyclerViewCursorAdapter<NotificationAdapter.ViewHolder> {

// Provide a suitable constructor (depends on the kind of dataset)
public NotificationAdapter(Notification[] myDataset) {
mDataset = myDataset;
NotificationAdapter(Cursor cursor) {
super(cursor);
}

// Create new views (invoked by the layout manager)
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
View layout = LayoutInflater.from(parent.getContext())
.inflate(R.layout.view_notification_item, parent, false);
return new ViewHolder(v);
return new ViewHolder(layout);
}

// Replace the contents of a view (invoked by the layout manager)
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Notification n = mDataset[position];
public void onBindViewHolder(ViewHolder holder, Cursor cursor) {
Notification n = Notification.fromCursor(cursor);
holder.id = n.getId();
holder.mBody.setText(n.getMessage());
String title = n.getTitle();
if (title != null && !title.isEmpty()) {
Expand All @@ -102,10 +127,16 @@ public void onBindViewHolder(ViewHolder holder, int position) {
}
}

// Return the size of your dataset (invoked by the layout manager)
@Override
public int getItemCount() {
return mDataset.length;
static class ViewHolder extends RecyclerView.ViewHolder {
public final TextView mTitle;
public final TextView mBody;
public long id;

public ViewHolder(View v) {
super(v);
mTitle = (TextView) v.findViewById(R.id.notification_title);
mBody = (TextView) v.findViewById(R.id.notification_body);
}
}
}
}
22 changes: 17 additions & 5 deletions app/src/main/java/com/g11x/checklistapp/data/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,16 @@ public static class Notification {

public static final String TITLE_COLUMN = "title";
public static final String MESSAGE_COLUMN = "message";
public static final String READ_COLUMN = "item_hash";
public static final String READ_COLUMN = "read";
public static final String SENT_TIME = "sent_time";

public static final String[] PROJECTION = {
Database.ID_COLUMN,
SENT_TIME,
READ_COLUMN,
TITLE_COLUMN,
MESSAGE_COLUMN
};

/**
* SQL statement for creating this table.
Expand All @@ -196,13 +205,13 @@ private static class NotificationTableHandler implements TableHandler {

@Override
public Uri insert(ContentValues contentValues) {
long id = db.insert(Database.ChecklistItem.TABLE_NAME, null, contentValues);
long id = db.insert(TABLE_NAME, null, contentValues);
return ContentUris.withAppendedId(contentUri, id);
}

@Override
public Cursor query(String[] projection, String selection, String[] selectionArgs, String sortOrder) {
return null;
return db.query(TABLE_NAME, projection, selection, selectionArgs, null, null, null);
}

@Override
Expand All @@ -213,11 +222,14 @@ public int update(ContentValues contentValues, String selection, String[] select

private static Uri createContentUri() {
// TODO: Figure out how to use getString(R.string.content_provider_authority) here.
return Uri.parse("content://" + COM_G11X_CHECKLISTAPP_PROVIDER + "/" + Database.NAME + "/" + TABLE_NAME);
return Uri.parse("content://" + COM_G11X_CHECKLISTAPP_PROVIDER + "/" + Database.NAME + "/" +
TABLE_NAME);
}

private static String createCreateTableSql() {
return "create table " + TABLE_NAME + " (_ID integer primary key, " + TITLE_COLUMN + " text, " + MESSAGE_COLUMN + " text, " + READ_COLUMN + " boolean);";
return "create table " + TABLE_NAME + " (" + ID_COLUMN + " integer primary key, " +
TITLE_COLUMN + " text, " + MESSAGE_COLUMN + " text, " + READ_COLUMN + " boolean, " +
SENT_TIME + " integer);";
}
}

Expand Down
50 changes: 47 additions & 3 deletions app/src/main/java/com/g11x/checklistapp/data/Notification.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,70 @@

package com.g11x.checklistapp.data;

import android.support.annotation.Nullable;
import android.database.Cursor;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

/**
* Stored representation of a Firebase notification.
*/

public class Notification {
@NonNull
private final Long id;

@NonNull
private final Long sentTime;

private final boolean read;

@Nullable
private final String title;

@NonNull
private final String message;

public Notification(@NonNull String message, @Nullable String title) {
public Notification(
@NonNull Long id,
@NonNull Long sentTime,
boolean read,
@Nullable String title,
@NonNull String message) {
this.id = id;
this.sentTime = sentTime;
this.read = read;
this.message = message;
this.title = title;
}

/**
* Convenience method to convert a {@code Cursor} to {@code Notification}. Field order found in
* {@code com.g11x.checklistapp.data.Database.Notification.PROJECTION}.
*/
public static
@NonNull
Notification fromCursor(Cursor cursor) {
return new Notification(
cursor.getLong(0),
cursor.getLong(1),
cursor.getShort(2) > 0,
cursor.getString(3),
cursor.getString(4));
}

@NonNull
public Long getId() {
return id;
}

@NonNull
public Long getSentTime() {
return sentTime;
}

public boolean isRead() {
return read;
}

@Nullable
public String getTitle() {
return title;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
package com.g11x.checklistapp.services;


import android.content.ContentValues;
import android.util.Log;

import com.g11x.checklistapp.data.Database;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;

Expand All @@ -30,16 +32,29 @@ public class NotificationService extends FirebaseMessagingService {

private static final String TAG = "NotificationService";

public void onMessageReceived (RemoteMessage message) {
public void onMessageReceived(RemoteMessage message) {
super.onMessageReceived(message);

RemoteMessage.Notification notification = message.getNotification();

Log.d(TAG, "Received notification: " + message.getNotification().getBody());

// TODO: Route message to DB, specifically message.getNotification().getBody() && getTitle().
// NOTE: Message expiration date not present in message payload, appears to only be server-side.

// TODO: Update notification view to be bound to DB and auto-reflect updates in reverse
// chronological order.
ContentValues newValues = new ContentValues();
newValues.put(Database.Notification.MESSAGE_COLUMN, notification.getBody());
newValues.put(Database.Notification.READ_COLUMN, false);
newValues.put(Database.Notification.SENT_TIME, message.getSentTime());
String title = notification.getTitle();
if (title != null && !title.isEmpty()) {
newValues.put(Database.Notification.TITLE_COLUMN, title);
}
getContentResolver().insert(
Database.Notification.CONTENT_URI,
newValues
);

// TODO: Provide some affordance to display the message when the app is in the foreground.
// See https://firebase.google.com/docs/cloud-messaging/android/receive for more details.
}

}
12 changes: 6 additions & 6 deletions app/src/main/res/layout/activity_notification_list.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"/>

<include layout="@layout/drawer" />
<include layout="@layout/drawer"/>
</android.support.v4.widget.DrawerLayout>
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.0-beta1'
classpath 'com.android.tools.build:gradle:2.2.0-beta2'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
Expand Down