Merge https://bdgit.educoder.net/p7tupf26b/git into merge-branch
commit
aff9ac63d1
@ -0,0 +1,137 @@
|
||||
package net.micode.notes.model;
|
||||
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.DataColumns;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.data.Notes.TextNote;
|
||||
|
||||
public class Task {
|
||||
private static final String TAG = "Task";
|
||||
|
||||
public long id;
|
||||
public String snippet; // Content
|
||||
public long createdDate;
|
||||
public long modifiedDate;
|
||||
public int priority; // 0=Low, 1=Mid, 2=High
|
||||
public long dueDate;
|
||||
public int status; // 0=Active, 1=Completed
|
||||
public long finishedTime;
|
||||
public long alertDate;
|
||||
|
||||
public static final int PRIORITY_LOW = 0;
|
||||
public static final int PRIORITY_NORMAL = 1;
|
||||
public static final int PRIORITY_HIGH = 2;
|
||||
|
||||
public static final int STATUS_ACTIVE = 0;
|
||||
public static final int STATUS_COMPLETED = 1;
|
||||
|
||||
public Task() {
|
||||
id = 0;
|
||||
snippet = "";
|
||||
createdDate = System.currentTimeMillis();
|
||||
modifiedDate = System.currentTimeMillis();
|
||||
priority = PRIORITY_LOW;
|
||||
dueDate = 0;
|
||||
status = STATUS_ACTIVE;
|
||||
finishedTime = 0;
|
||||
alertDate = 0;
|
||||
}
|
||||
|
||||
public static Task fromCursor(Cursor cursor) {
|
||||
Task task = new Task();
|
||||
|
||||
// Use getColumnIndex instead of getColumnIndexOrThrow for safety
|
||||
int idxId = cursor.getColumnIndex(NoteColumns.ID);
|
||||
if (idxId != -1) task.id = cursor.getLong(idxId);
|
||||
|
||||
int idxSnippet = cursor.getColumnIndex(NoteColumns.SNIPPET);
|
||||
if (idxSnippet != -1) task.snippet = cursor.getString(idxSnippet);
|
||||
|
||||
int idxCreated = cursor.getColumnIndex(NoteColumns.CREATED_DATE);
|
||||
if (idxCreated != -1) task.createdDate = cursor.getLong(idxCreated);
|
||||
|
||||
int idxModified = cursor.getColumnIndex(NoteColumns.MODIFIED_DATE);
|
||||
if (idxModified != -1) task.modifiedDate = cursor.getLong(idxModified);
|
||||
|
||||
int idxAlert = cursor.getColumnIndex(NoteColumns.ALERTED_DATE);
|
||||
if (idxAlert != -1) task.alertDate = cursor.getLong(idxAlert);
|
||||
|
||||
int idxPriority = cursor.getColumnIndex(NoteColumns.GTASK_PRIORITY);
|
||||
if (idxPriority != -1) task.priority = cursor.getInt(idxPriority);
|
||||
|
||||
int idxDueDate = cursor.getColumnIndex(NoteColumns.GTASK_DUE_DATE);
|
||||
if (idxDueDate != -1) task.dueDate = cursor.getLong(idxDueDate);
|
||||
|
||||
int idxStatus = cursor.getColumnIndex(NoteColumns.GTASK_STATUS);
|
||||
if (idxStatus != -1) task.status = cursor.getInt(idxStatus);
|
||||
|
||||
int idxFinished = cursor.getColumnIndex(NoteColumns.GTASK_FINISHED_TIME);
|
||||
if (idxFinished != -1) task.finishedTime = cursor.getLong(idxFinished);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
public Uri save(Context context) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(NoteColumns.TYPE, Notes.TYPE_TASK);
|
||||
values.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
|
||||
values.put(NoteColumns.ALERTED_DATE, alertDate);
|
||||
values.put(NoteColumns.GTASK_PRIORITY, priority);
|
||||
values.put(NoteColumns.GTASK_DUE_DATE, dueDate);
|
||||
values.put(NoteColumns.GTASK_STATUS, status);
|
||||
values.put(NoteColumns.GTASK_FINISHED_TIME, finishedTime);
|
||||
values.put(NoteColumns.LOCAL_MODIFIED, 1);
|
||||
|
||||
// Ensure snippet is updated in note table too, though trigger might handle it,
|
||||
// explicit update is safer if trigger fails or data table logic changes.
|
||||
values.put(NoteColumns.SNIPPET, snippet);
|
||||
|
||||
if (id == 0) {
|
||||
values.put(NoteColumns.CREATED_DATE, System.currentTimeMillis());
|
||||
values.put(NoteColumns.PARENT_ID, Notes.ID_ROOT_FOLDER);
|
||||
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
|
||||
if (uri != null) {
|
||||
id = ContentUris.parseId(uri);
|
||||
updateData(context);
|
||||
}
|
||||
return uri;
|
||||
} else {
|
||||
context.getContentResolver().update(
|
||||
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id),
|
||||
values, null, null
|
||||
);
|
||||
updateData(context);
|
||||
return ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateData(Context context) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DataColumns.NOTE_ID, id);
|
||||
values.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
|
||||
values.put(DataColumns.CONTENT, snippet);
|
||||
values.put(DataColumns.MODIFIED_DATE, System.currentTimeMillis());
|
||||
|
||||
Cursor c = context.getContentResolver().query(Notes.CONTENT_DATA_URI, new String[]{DataColumns.ID},
|
||||
DataColumns.NOTE_ID + "=?", new String[]{String.valueOf(id)}, null);
|
||||
|
||||
if (c != null) {
|
||||
if (c.moveToFirst()) {
|
||||
long dataId = c.getLong(0);
|
||||
context.getContentResolver().update(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), values, null, null);
|
||||
} else {
|
||||
context.getContentResolver().insert(Notes.CONTENT_DATA_URI, values);
|
||||
}
|
||||
c.close();
|
||||
} else {
|
||||
context.getContentResolver().insert(Notes.CONTENT_DATA_URI, values);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,205 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.TimePickerDialog;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RadioGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.model.Task;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class TaskEditActivity extends AppCompatActivity {
|
||||
|
||||
private EditText contentEdit;
|
||||
private ImageView alarmBtn;
|
||||
private ImageView tagBtn;
|
||||
private Button doneBtn;
|
||||
|
||||
private Task task;
|
||||
private long taskId;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_task_edit);
|
||||
|
||||
contentEdit = findViewById(R.id.task_edit_content);
|
||||
alarmBtn = findViewById(R.id.btn_alarm);
|
||||
tagBtn = findViewById(R.id.btn_tag);
|
||||
doneBtn = findViewById(R.id.btn_done);
|
||||
|
||||
Intent intent = getIntent();
|
||||
taskId = intent.getLongExtra(Intent.EXTRA_UID, 0);
|
||||
|
||||
if (taskId > 0) {
|
||||
loadTask();
|
||||
} else {
|
||||
task = new Task();
|
||||
}
|
||||
|
||||
setupListeners();
|
||||
}
|
||||
|
||||
private void loadTask() {
|
||||
new Thread(() -> {
|
||||
Cursor cursor = getContentResolver().query(
|
||||
Notes.CONTENT_NOTE_URI,
|
||||
null,
|
||||
NoteColumns.ID + "=?",
|
||||
new String[]{String.valueOf(taskId)},
|
||||
null
|
||||
);
|
||||
|
||||
if (cursor != null) {
|
||||
if (cursor.moveToFirst()) {
|
||||
task = Task.fromCursor(cursor);
|
||||
runOnUiThread(() -> {
|
||||
contentEdit.setText(task.snippet);
|
||||
contentEdit.setSelection(task.snippet.length());
|
||||
});
|
||||
} else {
|
||||
task = new Task();
|
||||
}
|
||||
cursor.close();
|
||||
} else {
|
||||
task = new Task();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void setupListeners() {
|
||||
doneBtn.setOnClickListener(v -> {
|
||||
saveTask();
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
});
|
||||
|
||||
alarmBtn.setOnClickListener(v -> {
|
||||
showAlarmDialog();
|
||||
});
|
||||
|
||||
tagBtn.setOnClickListener(v -> {
|
||||
showTagDialog();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (saveTask()) {
|
||||
setResult(RESULT_OK);
|
||||
}
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
private boolean saveTask() {
|
||||
String content = contentEdit.getText().toString();
|
||||
if (content.trim().length() == 0) {
|
||||
if (task.id == 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
task.snippet = content;
|
||||
task.save(this);
|
||||
|
||||
// Register Alarm if needed.
|
||||
if (task.alertDate > 0 && task.alertDate > System.currentTimeMillis()) {
|
||||
Intent intent = new Intent(this, AlarmReceiver.class);
|
||||
intent.setData(android.content.ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, task.id));
|
||||
android.app.PendingIntent pendingIntent = android.app.PendingIntent.getBroadcast(this, 0, intent, android.app.PendingIntent.FLAG_UPDATE_CURRENT | android.app.PendingIntent.FLAG_IMMUTABLE);
|
||||
android.app.AlarmManager alarmManager = (android.app.AlarmManager) getSystemService(ALARM_SERVICE);
|
||||
alarmManager.set(android.app.AlarmManager.RTC_WAKEUP, task.alertDate, pendingIntent);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void showAlarmDialog() {
|
||||
final Calendar c = Calendar.getInstance();
|
||||
if (task.alertDate > 0) {
|
||||
c.setTimeInMillis(task.alertDate);
|
||||
}
|
||||
|
||||
new DatePickerDialog(this, (view, year, month, dayOfMonth) -> {
|
||||
c.set(Calendar.YEAR, year);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
|
||||
new TimePickerDialog(this, (view1, hourOfDay, minute) -> {
|
||||
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
c.set(Calendar.SECOND, 0);
|
||||
|
||||
task.alertDate = c.getTimeInMillis();
|
||||
Toast.makeText(this, "Alarm set", Toast.LENGTH_SHORT).show();
|
||||
|
||||
}, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true).show();
|
||||
|
||||
}, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show();
|
||||
}
|
||||
|
||||
private void showTagDialog() {
|
||||
View view = LayoutInflater.from(this).inflate(R.layout.dialog_task_tag, null);
|
||||
|
||||
RadioGroup priorityGroup = view.findViewById(R.id.priority_group);
|
||||
TextView dateText = view.findViewById(R.id.date_text);
|
||||
Button dateBtn = view.findViewById(R.id.btn_set_date);
|
||||
|
||||
if (task.priority == Task.PRIORITY_HIGH) priorityGroup.check(R.id.priority_high);
|
||||
else if (task.priority == Task.PRIORITY_NORMAL) priorityGroup.check(R.id.priority_mid);
|
||||
else priorityGroup.check(R.id.priority_low);
|
||||
|
||||
final Calendar c = Calendar.getInstance();
|
||||
if (task.dueDate > 0) {
|
||||
c.setTimeInMillis(task.dueDate);
|
||||
dateText.setText(android.text.format.DateFormat.format("yyyy-MM-dd HH:mm", c));
|
||||
} else {
|
||||
dateText.setText("No Due Date");
|
||||
}
|
||||
|
||||
dateBtn.setOnClickListener(v -> {
|
||||
new DatePickerDialog(this, (dView, year, month, dayOfMonth) -> {
|
||||
c.set(Calendar.YEAR, year);
|
||||
c.set(Calendar.MONTH, month);
|
||||
c.set(Calendar.DAY_OF_MONTH, dayOfMonth);
|
||||
|
||||
new TimePickerDialog(this, (tView, hourOfDay, minute) -> {
|
||||
c.set(Calendar.HOUR_OF_DAY, hourOfDay);
|
||||
c.set(Calendar.MINUTE, minute);
|
||||
|
||||
task.dueDate = c.getTimeInMillis();
|
||||
dateText.setText(android.text.format.DateFormat.format("yyyy-MM-dd HH:mm", c));
|
||||
|
||||
}, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), true).show();
|
||||
|
||||
}, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show();
|
||||
});
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Set Tag")
|
||||
.setView(view)
|
||||
.setPositiveButton("OK", (dialog, which) -> {
|
||||
int id = priorityGroup.getCheckedRadioButtonId();
|
||||
if (id == R.id.priority_high) task.priority = Task.PRIORITY_HIGH;
|
||||
else if (id == R.id.priority_mid) task.priority = Task.PRIORITY_NORMAL;
|
||||
else task.priority = Task.PRIORITY_LOW;
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,156 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.data.Notes;
|
||||
import net.micode.notes.data.Notes.NoteColumns;
|
||||
import net.micode.notes.model.Task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TaskListActivity extends AppCompatActivity implements TaskListAdapter.OnTaskItemClickListener {
|
||||
|
||||
private RecyclerView recyclerView;
|
||||
private TaskListAdapter adapter;
|
||||
private FloatingActionButton fab;
|
||||
private static final int REQUEST_EDIT_TASK = 1001;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_task_list);
|
||||
|
||||
// Initialize Toolbar
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
// Remove default title
|
||||
if (getSupportActionBar() != null) {
|
||||
getSupportActionBar().setDisplayShowTitleEnabled(false);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
|
||||
}
|
||||
|
||||
// Setup custom "Notes" navigation
|
||||
View notesTitle = findViewById(R.id.tv_toolbar_title_notes);
|
||||
if (notesTitle != null) {
|
||||
notesTitle.setOnClickListener(v -> {
|
||||
finish();
|
||||
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
|
||||
});
|
||||
}
|
||||
|
||||
// Setup Navigation Icon (Back Button) - REMOVED as per new requirement
|
||||
// toolbar.setNavigationOnClickListener(v -> {
|
||||
// finish();
|
||||
// overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
|
||||
// });
|
||||
|
||||
recyclerView = findViewById(R.id.task_list_view);
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
adapter = new TaskListAdapter(this, this);
|
||||
recyclerView.setAdapter(adapter);
|
||||
|
||||
fab = findViewById(R.id.btn_new_task);
|
||||
fab.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(TaskListActivity.this, TaskEditActivity.class);
|
||||
startActivityForResult(intent, REQUEST_EDIT_TASK);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
loadTasks();
|
||||
}
|
||||
|
||||
private void loadTasks() {
|
||||
new Thread(() -> {
|
||||
Cursor cursor = getContentResolver().query(
|
||||
Notes.CONTENT_NOTE_URI,
|
||||
null,
|
||||
NoteColumns.TYPE + "=?",
|
||||
new String[]{String.valueOf(Notes.TYPE_TASK)},
|
||||
null
|
||||
);
|
||||
|
||||
List<Task> tasks = new ArrayList<>();
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
tasks.add(Task.fromCursor(cursor));
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
android.util.Log.d("TaskListActivity", "Loaded tasks count: " + tasks.size());
|
||||
|
||||
runOnUiThread(() -> adapter.setTasks(tasks));
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(Task task) {
|
||||
Intent intent = new Intent(this, TaskEditActivity.class);
|
||||
intent.putExtra(Intent.EXTRA_UID, task.id);
|
||||
startActivityForResult(intent, REQUEST_EDIT_TASK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCheckBoxClick(Task task) {
|
||||
task.status = (task.status == Task.STATUS_ACTIVE) ? Task.STATUS_COMPLETED : Task.STATUS_ACTIVE;
|
||||
if (task.status == Task.STATUS_COMPLETED) {
|
||||
task.finishedTime = System.currentTimeMillis();
|
||||
} else {
|
||||
task.finishedTime = 0;
|
||||
}
|
||||
|
||||
new Thread(() -> {
|
||||
task.save(this);
|
||||
runOnUiThread(() -> loadTasks()); // Reload to sort
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(android.view.Menu menu) {
|
||||
// Removed menu_notes as per requirement, keeping empty or future menus
|
||||
// getMenuInflater().inflate(R.menu.task_list, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_right);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == REQUEST_EDIT_TASK && resultCode == RESULT_OK) {
|
||||
loadTasks();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,168 @@
|
||||
package net.micode.notes.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import net.micode.notes.R;
|
||||
import net.micode.notes.model.Task;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class TaskListAdapter extends RecyclerView.Adapter<TaskListAdapter.TaskViewHolder> {
|
||||
|
||||
private List<Task> tasks = new ArrayList<>();
|
||||
private Context context;
|
||||
private OnTaskItemClickListener listener;
|
||||
|
||||
public interface OnTaskItemClickListener {
|
||||
void onItemClick(Task task);
|
||||
void onCheckBoxClick(Task task);
|
||||
}
|
||||
|
||||
public TaskListAdapter(Context context, OnTaskItemClickListener listener) {
|
||||
this.context = context;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setTasks(List<Task> newTasks) {
|
||||
this.tasks = new ArrayList<>(newTasks);
|
||||
sortTasks();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void sortTasks() {
|
||||
Collections.sort(tasks, new Comparator<Task>() {
|
||||
@Override
|
||||
public int compare(Task t1, Task t2) {
|
||||
// 1. Status: Active (0) < Completed (1)
|
||||
if (t1.status != t2.status) {
|
||||
return Integer.compare(t1.status, t2.status);
|
||||
}
|
||||
|
||||
// 2. If both Active
|
||||
if (t1.status == Task.STATUS_ACTIVE) {
|
||||
// Priority: High (2) > Mid (1) > Low (0) -> DESC
|
||||
if (t1.priority != t2.priority) {
|
||||
return Integer.compare(t2.priority, t1.priority);
|
||||
}
|
||||
|
||||
if (t1.dueDate != t2.dueDate) {
|
||||
if (t1.dueDate == 0) return 1; // t1 no date -> bottom
|
||||
if (t2.dueDate == 0) return -1; // t2 no date -> bottom
|
||||
return Long.compare(t1.dueDate, t2.dueDate); // Early date first
|
||||
}
|
||||
|
||||
// Creation Date (Fallback)
|
||||
return Long.compare(t2.createdDate, t1.createdDate);
|
||||
}
|
||||
|
||||
// 3. If both Completed
|
||||
// DESC sort by finishedTime.
|
||||
return Long.compare(t2.finishedTime, t1.finishedTime);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public TaskViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.task_list_item, parent, false);
|
||||
return new TaskViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull TaskViewHolder holder, int position) {
|
||||
Task task = tasks.get(position);
|
||||
holder.bind(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return tasks.size();
|
||||
}
|
||||
|
||||
class TaskViewHolder extends RecyclerView.ViewHolder {
|
||||
CheckBox checkBox;
|
||||
TextView content;
|
||||
TextView priority;
|
||||
TextView date;
|
||||
ImageView alarm;
|
||||
|
||||
public TaskViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
checkBox = itemView.findViewById(R.id.task_checkbox);
|
||||
content = itemView.findViewById(R.id.task_content);
|
||||
priority = itemView.findViewById(R.id.task_priority);
|
||||
date = itemView.findViewById(R.id.task_date);
|
||||
alarm = itemView.findViewById(R.id.task_alarm_icon);
|
||||
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onItemClick(tasks.get(getAdapterPosition()));
|
||||
}
|
||||
});
|
||||
|
||||
checkBox.setOnClickListener(v -> {
|
||||
if (getAdapterPosition() != RecyclerView.NO_POSITION) {
|
||||
listener.onCheckBoxClick(tasks.get(getAdapterPosition()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void bind(Task task) {
|
||||
content.setText(task.snippet);
|
||||
checkBox.setChecked(task.status == Task.STATUS_COMPLETED);
|
||||
|
||||
if (task.status == Task.STATUS_COMPLETED) {
|
||||
content.setPaintFlags(content.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
content.setAlpha(0.5f);
|
||||
} else {
|
||||
content.setPaintFlags(content.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
|
||||
content.setAlpha(1.0f);
|
||||
}
|
||||
|
||||
// Priority
|
||||
if (task.priority == Task.PRIORITY_HIGH) {
|
||||
priority.setVisibility(View.VISIBLE);
|
||||
priority.setText("HIGH");
|
||||
priority.setBackgroundColor(0xFFFFCDD2); // Light Red
|
||||
priority.setTextColor(0xFFB71C1C); // Dark Red
|
||||
} else if (task.priority == Task.PRIORITY_NORMAL) {
|
||||
priority.setVisibility(View.VISIBLE);
|
||||
priority.setText("MED");
|
||||
priority.setBackgroundColor(0xFFFFF9C4); // Light Yellow
|
||||
priority.setTextColor(0xFFF57F17); // Dark Yellow
|
||||
} else {
|
||||
priority.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Due Date
|
||||
if (task.dueDate > 0) {
|
||||
date.setVisibility(View.VISIBLE);
|
||||
date.setText(DateFormat.format("MM/dd HH:mm", task.dueDate));
|
||||
} else {
|
||||
date.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// Alarm
|
||||
if (task.alertDate > 0) {
|
||||
alarm.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
alarm.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="-100%"
|
||||
android:toXDelta="0%"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"/>
|
||||
</set>
|
||||
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="300"
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="100%"
|
||||
android:toXDelta="0%" />
|
||||
</set>
|
||||
android:toXDelta="0%"
|
||||
android:interpolator="@android:anim/decelerate_interpolator"/>
|
||||
</set>
|
||||
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="300"
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="-100%" />
|
||||
</set>
|
||||
android:toXDelta="-100%"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"/>
|
||||
</set>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<translate
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="100%"
|
||||
android:interpolator="@android:anim/accelerate_interpolator"/>
|
||||
</set>
|
||||
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14,2H6c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2H18c1.1,0 2,-0.9 2,-2V8l-6,-6zM16,18H8v-2h8v2zM16,14H8v-2h8v2zM13,9V3.5L18.5,9H13z"/>
|
||||
</vector>
|
||||
@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
|
||||
</vector>
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2H5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM14,17H7v-2h7v2zM17,13H7v-2h10v2zM17,9H7V7h10v2z"/>
|
||||
</vector>
|
||||
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:background="#F0F0F0"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_alarm"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@android:drawable/ic_lock_idle_alarm"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:clickable="true"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/btn_tag"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:src="@drawable/ic_menu_rich_text"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:clickable="true"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"/>
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_done"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Done" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<EditText
|
||||
android:id="@+id/task_edit_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/bottom_bar"
|
||||
android:gravity="top|start"
|
||||
android:padding="16dp"
|
||||
android:background="@null"
|
||||
android:hint="Enter task..."
|
||||
android:textSize="18sp" />
|
||||
|
||||
</RelativeLayout>
|
||||
@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/background_color">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/ThemeOverlay.Material3.ActionBar"
|
||||
android:fitsSystemWindows="true">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:contentInsetStart="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_toolbar_title_notes"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Notes"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true" />
|
||||
|
||||
</com.google.android.material.appbar.MaterialToolbar>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/task_list_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="80dp"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/btn_new_task"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:src="@drawable/ic_add"
|
||||
app:backgroundTint="@color/fab_color"
|
||||
app:tint="@color/text_color_primary"
|
||||
android:contentDescription="New Task" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Priority"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/priority_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/priority_low"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Low"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/priority_mid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Mid"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/priority_high"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="High"
|
||||
android:layout_weight="1"/>
|
||||
</RadioGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Due Date"
|
||||
android:textStyle="bold"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/date_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="No Due Date" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_set_date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Set" />
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@ -0,0 +1,70 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:padding="12dp"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@color/bg_white"
|
||||
android:layout_marginBottom="1dp">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/task_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingEnd="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/task_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/text_color_primary"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="2" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginTop="4dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/task_priority"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingEnd="4dp"
|
||||
android:background="#FFCDD2"
|
||||
android:textColor="#B71C1C"
|
||||
android:text="HIGH"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/task_date"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="12sp"
|
||||
android:textColor="@color/text_color_secondary" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/task_alarm_icon"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="16dp"
|
||||
android:src="@android:drawable/ic_lock_idle_alarm"
|
||||
android:tint="@color/text_color_secondary"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
Loading…
Reference in new issue