Ahmad Arif Faizin
6 min readSep 20, 2017

--

U-GDC-TME

activity_main.xml — go to AddTaskActivity

/* Click events in Floating Action Button */<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/activity_horizontal_margin"
app:srcCompat="@drawable/ic_add"
android:onClick="onClick"
/>android:onClick="onClick"

TaskProvider.java — Content Provider

package com.google.developer.taskmaker.data;

import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.Nullable;
import android.util.Log;

public class TaskProvider extends ContentProvider {
private static final String TAG = TaskProvider.class.getSimpleName();

private static final int CLEANUP_JOB_ID = 43;

private static final int TASKS = 100;
private static final int TASKS_WITH_ID = 101;

private TaskDbHelper mDbHelper;

private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
// content://com.google.developer.taskmaker/tasks
sUriMatcher.addURI(DatabaseContract.CONTENT_AUTHORITY,
DatabaseContract.TABLE_TASKS,
TASKS);

// content://com.google.developer.taskmaker/tasks/id
sUriMatcher.addURI(DatabaseContract.CONTENT_AUTHORITY,
DatabaseContract.TABLE_TASKS + "/#",
TASKS_WITH_ID);
}

@Override
public boolean onCreate() {
mDbHelper = new TaskDbHelper(getContext());
manageCleanupJob();
return true;
}

@Nullable
@Override
public String getType(Uri uri) {
return null; /* Not used */
}

@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
//TODO: Implement task query
//TODO: Expected "query all" Uri: content://com.google.developer.taskmaker/tasks
//TODO: Expected "query one" Uri: content://com.google.developer.taskmaker/tasks/{id}
Cursor returnCursor;
SQLiteDatabase db = mDbHelper.getReadableDatabase();
switch (sUriMatcher.match(uri)) {
case TASKS:
returnCursor = db.query(
DatabaseContract.TABLE_TASKS,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
break;

case TASKS_WITH_ID:
returnCursor = db.query(
DatabaseContract.TABLE_TASKS,
projection,
DatabaseContract.TaskColumns._ID + " = ?",
new String[]{uri.getLastPathSegment()},
null,
null,
sortOrder
);

break;
default:
throw new UnsupportedOperationException("Unknown URI:" + uri);
}

Context context = getContext();
if (context != null){
returnCursor.setNotificationUri(context.getContentResolver(), uri);
}
return returnCursor;
}

@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
//TODO: Implement new task insert
//TODO: Expected Uri: content://com.google.developer.taskmaker/tasks
SQLiteDatabase db = mDbHelper.getWritableDatabase();
Uri returnUri;

switch (sUriMatcher.match(uri)) {
case TASKS:
db.insert(
DatabaseContract.TABLE_TASKS,
null,
values
);
returnUri = DatabaseContract.CONTENT_URI;
break;
default:
throw new UnsupportedOperationException("Unknown URI:" + uri);
}

Context context = getContext();
if (context != null){
context.getContentResolver().notifyChange(uri, null);
}

return returnUri;
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
//TODO: Implement existing task update
//TODO: Expected Uri: content://com.google.developer.taskmaker/tasks/{id}
switch
(sUriMatcher.match(uri)) {
case TASKS_WITH_ID:
long id = ContentUris.parseId(uri);
selection = String.format("%s = ?", DatabaseContract.TaskColumns._ID);
selectionArgs = new String[]{String.valueOf(id)};
break;
default:
throw new IllegalArgumentException("Illegal delete URI");
}

SQLiteDatabase db = mDbHelper.getWritableDatabase();
int count = db.update(DatabaseContract.TABLE_TASKS,values,selection,selectionArgs);

if (count > 0) {
//Notify observers of the change
getContext().getContentResolver().notifyChange(uri, null);
}

return count;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {

switch (sUriMatcher.match(uri)) {
case TASKS:
//Rows aren't counted with null selection
selection = (selection == null) ? "1" : selection;
break;
case TASKS_WITH_ID:
long id = ContentUris.parseId(uri);
selection = String.format("%s = ?", DatabaseContract.TaskColumns._ID);
selectionArgs = new String[]{String.valueOf(id)};
break;
default:
throw new IllegalArgumentException("Illegal delete URI");
}

SQLiteDatabase db = mDbHelper.getWritableDatabase();
int count = db.delete(DatabaseContract.TABLE_TASKS, selection, selectionArgs);

if (count > 0) {
//Notify observers of the change
getContext().getContentResolver().notifyChange(uri, null);
}

return count;
}

/* Initiate a periodic job to clear out completed items */
private void manageCleanupJob() {
Log.d(TAG, "Scheduling cleanup job");
JobScheduler jobScheduler = (JobScheduler) getContext()
.getSystemService(Context.JOB_SCHEDULER_SERVICE);

//Run the job approximately every hour
long jobInterval = 900000L;

ComponentName jobService = new ComponentName(getContext(), CleanupJobService.class);
JobInfo task = new JobInfo.Builder(CLEANUP_JOB_ID, jobService)
.setPeriodic(jobInterval)
.setPersisted(true)
.build();

if (jobScheduler.schedule(task) != JobScheduler.RESULT_SUCCESS) {
Log.w(TAG, "Unable to schedule cleanup job");
}
}
}

MainActivity.java — Load Data

implements
TaskAdapter.OnItemClickListener,
View.OnClickListener,
LoaderManager.LoaderCallbacks<Cursor>
private static final int ID_TASK_LOADER = 0;getSupportLoaderManager().initLoader(ID_TASK_LOADER, null, this);@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this,
DatabaseContract.CONTENT_URI,
null,
null,
null,
null);
}

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}

@Override
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}

TaskAdapter.java — Set Data

@Override
public void onBindViewHolder(TaskHolder holder, int position) {

//TODO: Bind the task data to the views
int
idIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns._ID);
int checkboxIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns.IS_COMPLETE);
int priorityIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns.IS_PRIORITY);
int dueDateIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns.DUE_DATE);
int taskDescriptionIndex = mCursor.getColumnIndex(DatabaseContract.TaskColumns.DESCRIPTION);

mCursor.moveToPosition(position);

final int id = mCursor.getInt(idIndex);
int isComplete = mCursor.getInt(checkboxIndex);
int priority = mCursor.getInt(priorityIndex);
long dueDate = Long.parseLong(mCursor.getString(dueDateIndex));
String taskDescrition = mCursor.getString(taskDescriptionIndex);

Log.d("TaskAdapter", "onBindViewHolder: " + id + isComplete + priority + "+" + dueDate + taskDescrition);

holder.itemView.setTag(id);

if (isComplete == 1) {
holder.checkBox.setChecked(true);
holder.nameView.setState(TaskTitleView.DONE);
holder.nameView.setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG);
} else if (dueDate < System.currentTimeMillis()) {
holder.nameView.setState(TaskTitleView.OVERDUE);
holder.checkBox.setChecked(false);
} else {
holder.nameView.setState(TaskTitleView.NORMAL);
holder.checkBox.setChecked(false);
}

if (priority == 1) {
holder.priorityView.setImageResource(R.drawable.ic_priority);
} else {
holder.priorityView.setImageResource(R.drawable.ic_not_priority);
}

if (dueDate == Long.MAX_VALUE) {
holder.dateView.setText(R.string.date_empty);
} else {
CharSequence formatted = DateUtils.getRelativeTimeSpanString(mContext, dueDate);
holder.dateView.setVisibility(View.VISIBLE);
holder.dateView.setText(formatted);
}

holder.nameView.setText(taskDescrition);


}

SortOrder

@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this,
DatabaseContract.CONTENT_URI,
null,
null,
null,
getOrder());
}
private final String getOrder(){
// Retrieve the order
String order = PreferenceManager.getDefaultSharedPreferences(this).getString(getString(R.string.pref_sortBy_key), getString(R.string.pref_sortBy_default));
Log.d("MainActivity", "pref: "+ order);
String orderToSend=null;
if(order.equals(getString(R.string.pref_sortBy_default))){
orderToSend = DatabaseContract.DEFAULT_SORT;
Log.d("MainActivity", "getOrder: default");
}else{
orderToSend = DatabaseContract.DATE_SORT;
Log.d("MainActivity", "getOrder: date");
}
return orderToSend;
}

.Supaya ketika kembali dari setting data langsung terupdate

@Override
protected void onResume() {
super.onResume();
getSupportLoaderManager().restartLoader(ID_TASK_LOADER, null, this);
}

Update ketika Check

/* Click events on RecyclerView item checkboxes */
@Override
public void onItemToggled(boolean active, int position) {
//TODO: Handle task item checkbox event
ContentValues cv = new ContentValues();
if(active){
cv.put(DatabaseContract.TaskColumns.IS_COMPLETE,"1");
}else{
cv.put(DatabaseContract.TaskColumns.IS_COMPLETE,"0");
}

Log.d(TAG, "onItemToggled: id "+ mAdapter.getItemId(position));
TaskUpdateService.updateTask(this,
ContentUris.withAppendedId(DatabaseContract.CONTENT_URI,mAdapter.getItemId(position)),
cv);
}

Kirim data uri ke Detail Task

/* Click events in RecyclerView items */
@Override
public void onItemClick(View v, int position) {
//TODO: Handle list item click event
Intent detailIntent = new Intent(getBaseContext(),TaskDetailActivity.class);
detailIntent.setData(ContentUris.withAppendedId(DatabaseContract.CONTENT_URI,mAdapter.getItemId(position)));
startActivity(detailIntent);
}

DetailTaskActivity.java — Show detail data

private static final String TAG = "TaskDetailActivity";

private TaskTitleView textDescription;
private TextView textDate;
private ImageView imagepriority;
Uri mUri;
private Cursor mDetailCursor;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail_task);
//Task must be passed to this activity as a valid provider Uri
final Uri taskUri = getIntent().getData();
mUri = taskUri;
Log.d(TAG, "taskUri: " + taskUri);
//TODO: Display attributes of the provided task in the UI

initView();
getData();

}

private void initView() {
textDescription = (TaskTitleView) findViewById(R.id.text_description);
textDate = (TextView) findViewById(R.id.text_date);
imagepriority = (ImageView) findViewById(R.id.priority);
}

private void getData() {
mDetailCursor = getContentResolver().query(
mUri,
null,
null,
null,
null);

int checkboxIndex = mDetailCursor.getColumnIndex(DatabaseContract.TaskColumns.IS_COMPLETE);
int priorityIndex = mDetailCursor.getColumnIndex(DatabaseContract.TaskColumns.IS_PRIORITY);
int dueDateIndex = mDetailCursor.getColumnIndex(DatabaseContract.TaskColumns.DUE_DATE);
int taskDescriptionIndex = mDetailCursor.getColumnIndex(DatabaseContract.TaskColumns.DESCRIPTION);

mDetailCursor.moveToFirst();

int isComplete = mDetailCursor.getInt(checkboxIndex);
int priority = mDetailCursor.getInt(priorityIndex);
long dueDate = Long.parseLong(mDetailCursor.getString(dueDateIndex));
String taskDescrition = mDetailCursor.getString(taskDescriptionIndex);

Log.d(TAG, "taskdetail: " + isComplete + priority + "+" + dueDate + taskDescrition);


if (priority == 1) {
imagepriority.setImageResource(R.drawable.ic_priority);
} else {
imagepriority.setImageResource(R.drawable.ic_not_priority);
}

if (dueDate == Long.MAX_VALUE) {
textDate.setText(R.string.date_empty);
} else {
CharSequence formatted = DateUtils.getRelativeTimeSpanString(this, dueDate);
textDate.setVisibility(View.VISIBLE);
textDate.setText("Due Date : "+formatted);
}

textDescription.setText(taskDescrition);
}

Delete and Schedule Reminder

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete:
TaskUpdateService.deleteTask(this, mUri);
finish();
break;
case R.id.action_reminder:
DatePickerFragment datePickerFragment = new DatePickerFragment();
datePickerFragment.show(getSupportFragmentManager(), "datePicker");
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}

@Override
public void onDateSet(DatePicker view, int year, int month, int day) {
//TODO: Handle date selection from a DatePickerFragment
Calendar c2 = Calendar.getInstance();
c2.set(Calendar.HOUR_OF_DAY, 12);
c2.set(Calendar.MINUTE, 0);
c2.set(Calendar.SECOND, 0);

Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
c.set(Calendar.HOUR_OF_DAY, 12);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
view.setMinDate(c.getTimeInMillis());

if (c.getTimeInMillis() < c2.getTimeInMillis()) {
Toast.makeText(this, R.string.date_error, Toast.LENGTH_SHORT).show();
return;
}
Toast.makeText(this, "Alarm scheduled to " + DateUtils.getRelativeTimeSpanString(this, c.getTimeInMillis()), Toast.LENGTH_SHORT).show();
AlarmScheduler.scheduleAlarm(getApplicationContext(), c.getTimeInMillis(), mUri);
}

Notification Intent to Detail

//Display a notification to view the task details
Intent action = new Intent(this, TaskDetailActivity.class);
action.setData(uri);
PendingIntent operation = TaskStackBuilder.create(this)
.addNextIntentWithParentStack(action)
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

CleanJob setiap satu jam

private void manageCleanupJob() {
Log.d(TAG, "Scheduling cleanup job");
JobScheduler jobScheduler = (JobScheduler) getContext()
.getSystemService(Context.JOB_SCHEDULER_SERVICE);

//Run the job approximately every hour
long jobInterval = 3600000L;

ComponentName jobService = new ComponentName(getContext(), CleanupJobService.class);
JobInfo task = new JobInfo.Builder(CLEANUP_JOB_ID, jobService)
.setPeriodic(jobInterval)
.setPersisted(true)
.build();

if (jobScheduler.schedule(task) != JobScheduler.RESULT_SUCCESS) {
Log.w(TAG, "Unable to schedule cleanup job");
}
}

UI Testing

@RunWith(AndroidJUnit4.class)
public class UiTesting {
@Rule
public IntentsTestRule<MainActivity> mActivityTestRule = new IntentsTestRule<>(MainActivity.class);

@Test
public void intentAddTask() {
onView(withId(R.id.fab)).perform(click());
intended(hasComponent(AddTaskActivity.class.getName()));
}

}

Break the through

public void setState(int state) {
switch (state) {
case DONE:
setPaintFlags(Paint.STRIKE_THRU_TEXT_FLAG);
setTextColor(ContextCompat.getColor(getContext(), R.color.black));
break;
case NORMAL:
setPaintFlags(0);
setTextColor(ContextCompat.getColor(getContext(), R.color.black));
break;
case OVERDUE:
setPaintFlags(0);
setTextColor(ContextCompat.getColor(getContext(), R.color.red));
break;
default:
return;
}

mState = state;
}

--

--