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;
}