fix gitignore
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.example.todojava"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.example.todojava"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.material)
|
||||
implementation(libs.room.runtime)
|
||||
annotationProcessor(libs.room.compiler)
|
||||
implementation(libs.lifecycle.viewmodel)
|
||||
implementation(libs.lifecycle.livedata)
|
||||
implementation(libs.recyclerview)
|
||||
implementation(libs.appcompat)
|
||||
implementation(libs.constraintlayout)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.TodoJava">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.example.todojava;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.example.todojava.data.Todo;
|
||||
import com.example.todojava.databinding.ActivityMainBinding;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
||||
private ActivityMainBinding binding;
|
||||
private TodoViewModel viewModel;
|
||||
private TodoAdapter adapter;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
setSupportActionBar(binding.toolbar);
|
||||
|
||||
viewModel = new ViewModelProvider(this).get(TodoViewModel.class);
|
||||
|
||||
setupRecyclerView();
|
||||
setupFilterChips();
|
||||
setupFab();
|
||||
observeData();
|
||||
}
|
||||
|
||||
private void setupRecyclerView() {
|
||||
adapter = new TodoAdapter();
|
||||
binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.recyclerView.setAdapter(adapter);
|
||||
|
||||
adapter.setOnTodoClickListener(todo -> {
|
||||
TodoFormDialogFragment dialog = TodoFormDialogFragment.newInstance(todo);
|
||||
dialog.show(getSupportFragmentManager(), "edit_todo");
|
||||
});
|
||||
|
||||
adapter.setOnTodoCheckedListener(todo -> viewModel.toggleCompleted(todo));
|
||||
|
||||
// Swipe to delete
|
||||
SwipeToDeleteCallback swipeCallback = new SwipeToDeleteCallback(position -> {
|
||||
Todo todo = adapter.getCurrentList().get(position);
|
||||
viewModel.delete(todo);
|
||||
Snackbar.make(binding.getRoot(), R.string.todo_deleted, Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.undo, v -> viewModel.insert(todo))
|
||||
.show();
|
||||
});
|
||||
new ItemTouchHelper(swipeCallback).attachToRecyclerView(binding.recyclerView);
|
||||
}
|
||||
|
||||
private void setupFilterChips() {
|
||||
binding.chipAll.setOnClickListener(v -> viewModel.setFilter(TodoViewModel.Filter.ALL));
|
||||
binding.chipActive.setOnClickListener(v -> viewModel.setFilter(TodoViewModel.Filter.ACTIVE));
|
||||
binding.chipDone.setOnClickListener(v -> viewModel.setFilter(TodoViewModel.Filter.DONE));
|
||||
}
|
||||
|
||||
private void setupFab() {
|
||||
binding.fab.setOnClickListener(v -> {
|
||||
TodoFormDialogFragment dialog = TodoFormDialogFragment.newInstance();
|
||||
dialog.show(getSupportFragmentManager(), "add_todo");
|
||||
});
|
||||
}
|
||||
|
||||
private void observeData() {
|
||||
viewModel.getFilteredTodos().observe(this, todos -> {
|
||||
adapter.submitList(todos);
|
||||
updateEmptyState(todos);
|
||||
});
|
||||
|
||||
// Update chip text with counts
|
||||
viewModel.getAllCount().observe(this, count -> {
|
||||
String text = getString(R.string.filter_all) + " (" + count + ")";
|
||||
binding.chipAll.setText(text);
|
||||
});
|
||||
|
||||
viewModel.getActiveCount().observe(this, count -> {
|
||||
String text = getString(R.string.filter_active) + " (" + count + ")";
|
||||
binding.chipActive.setText(text);
|
||||
});
|
||||
|
||||
viewModel.getCompletedCount().observe(this, count -> {
|
||||
String text = getString(R.string.filter_done) + " (" + count + ")";
|
||||
binding.chipDone.setText(text);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateEmptyState(List<Todo> todos) {
|
||||
boolean isEmpty = todos == null || todos.isEmpty();
|
||||
binding.emptyState.getRoot().setVisibility(isEmpty ? View.VISIBLE : View.GONE);
|
||||
binding.recyclerView.setVisibility(isEmpty ? View.GONE : View.VISIBLE);
|
||||
|
||||
if (isEmpty) {
|
||||
TodoViewModel.Filter filter = viewModel.getCurrentFilter().getValue();
|
||||
if (filter == null) filter = TodoViewModel.Filter.ALL;
|
||||
|
||||
View emptyRoot = binding.emptyState.getRoot();
|
||||
android.widget.TextView titleView = emptyRoot.findViewById(R.id.emptyTitle);
|
||||
android.widget.TextView subtitleView = emptyRoot.findViewById(R.id.emptySubtitle);
|
||||
|
||||
switch (filter) {
|
||||
case ACTIVE:
|
||||
titleView.setText(R.string.empty_active_title);
|
||||
subtitleView.setText(R.string.empty_active_subtitle);
|
||||
break;
|
||||
case DONE:
|
||||
titleView.setText(R.string.empty_done_title);
|
||||
subtitleView.setText(R.string.empty_done_subtitle);
|
||||
break;
|
||||
default:
|
||||
titleView.setText(R.string.empty_state_title);
|
||||
subtitleView.setText(R.string.empty_state_subtitle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.example.todojava;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class SwipeToDeleteCallback extends ItemTouchHelper.SimpleCallback {
|
||||
|
||||
public interface OnSwipeListener {
|
||||
void onSwiped(int position);
|
||||
}
|
||||
|
||||
private final OnSwipeListener listener;
|
||||
private final ColorDrawable background;
|
||||
private final Paint textPaint;
|
||||
|
||||
public SwipeToDeleteCallback(OnSwipeListener listener) {
|
||||
super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
|
||||
this.listener = listener;
|
||||
this.background = new ColorDrawable(Color.parseColor("#E53935"));
|
||||
this.textPaint = new Paint();
|
||||
this.textPaint.setColor(Color.WHITE);
|
||||
this.textPaint.setTextSize(40f);
|
||||
this.textPaint.setAntiAlias(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView,
|
||||
@NonNull RecyclerView.ViewHolder viewHolder,
|
||||
@NonNull RecyclerView.ViewHolder target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
listener.onSwiped(viewHolder.getAdapterPosition());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChildDraw(@NonNull Canvas c,
|
||||
@NonNull RecyclerView recyclerView,
|
||||
@NonNull RecyclerView.ViewHolder viewHolder,
|
||||
float dX, float dY,
|
||||
int actionState, boolean isCurrentlyActive) {
|
||||
|
||||
View itemView = viewHolder.itemView;
|
||||
|
||||
if (dX > 0) {
|
||||
// Swiping right
|
||||
background.setBounds(itemView.getLeft(), itemView.getTop(),
|
||||
itemView.getLeft() + (int) dX, itemView.getBottom());
|
||||
} else if (dX < 0) {
|
||||
// Swiping left
|
||||
background.setBounds(itemView.getRight() + (int) dX, itemView.getTop(),
|
||||
itemView.getRight(), itemView.getBottom());
|
||||
} else {
|
||||
background.setBounds(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
background.draw(c);
|
||||
|
||||
// Draw "Delete" text
|
||||
String deleteText = "Delete";
|
||||
float textWidth = textPaint.measureText(deleteText);
|
||||
float textY = itemView.getTop() + (itemView.getHeight() + textPaint.getTextSize()) / 2f - 4f;
|
||||
|
||||
if (dX > 0) {
|
||||
float textX = itemView.getLeft() + 32f;
|
||||
c.drawText(deleteText, textX, textY, textPaint);
|
||||
} else if (dX < 0) {
|
||||
float textX = itemView.getRight() - textWidth - 32f;
|
||||
c.drawText(deleteText, textX, textY, textPaint);
|
||||
}
|
||||
|
||||
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.example.todojava;
|
||||
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.DiffUtil;
|
||||
import androidx.recyclerview.widget.ListAdapter;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.example.todojava.data.Todo;
|
||||
import com.example.todojava.databinding.ItemTodoBinding;
|
||||
|
||||
public class TodoAdapter extends ListAdapter<Todo, TodoAdapter.TodoViewHolder> {
|
||||
|
||||
public interface OnTodoClickListener {
|
||||
void onTodoClick(Todo todo);
|
||||
}
|
||||
|
||||
public interface OnTodoCheckedListener {
|
||||
void onTodoChecked(Todo todo);
|
||||
}
|
||||
|
||||
private OnTodoClickListener clickListener;
|
||||
private OnTodoCheckedListener checkedListener;
|
||||
|
||||
public TodoAdapter() {
|
||||
super(DIFF_CALLBACK);
|
||||
}
|
||||
|
||||
public void setOnTodoClickListener(OnTodoClickListener listener) {
|
||||
this.clickListener = listener;
|
||||
}
|
||||
|
||||
public void setOnTodoCheckedListener(OnTodoCheckedListener listener) {
|
||||
this.checkedListener = listener;
|
||||
}
|
||||
|
||||
private static final DiffUtil.ItemCallback<Todo> DIFF_CALLBACK =
|
||||
new DiffUtil.ItemCallback<Todo>() {
|
||||
@Override
|
||||
public boolean areItemsTheSame(@NonNull Todo oldItem, @NonNull Todo newItem) {
|
||||
return oldItem.getId() == newItem.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areContentsTheSame(@NonNull Todo oldItem, @NonNull Todo newItem) {
|
||||
return oldItem.getTitle().equals(newItem.getTitle())
|
||||
&& oldItem.isCompleted() == newItem.isCompleted()
|
||||
&& oldItem.getCategory().equals(newItem.getCategory())
|
||||
&& oldItem.getPriority().equals(newItem.getPriority());
|
||||
}
|
||||
};
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public TodoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ItemTodoBinding binding = ItemTodoBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new TodoViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull TodoViewHolder holder, int position) {
|
||||
Todo todo = getItem(position);
|
||||
holder.bind(todo);
|
||||
}
|
||||
|
||||
class TodoViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ItemTodoBinding binding;
|
||||
|
||||
TodoViewHolder(ItemTodoBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
void bind(Todo todo) {
|
||||
binding.textTitle.setText(todo.getTitle());
|
||||
binding.chipCategory.setText(todo.getCategory());
|
||||
|
||||
// Handle completed state
|
||||
binding.checkBox.setOnCheckedChangeListener(null);
|
||||
binding.checkBox.setChecked(todo.isCompleted());
|
||||
|
||||
if (todo.isCompleted()) {
|
||||
binding.textTitle.setPaintFlags(
|
||||
binding.textTitle.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
binding.textTitle.setTextColor(
|
||||
ContextCompat.getColor(itemView.getContext(), R.color.completed_text));
|
||||
} else {
|
||||
binding.textTitle.setPaintFlags(
|
||||
binding.textTitle.getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
binding.textTitle.setTextColor(
|
||||
ContextCompat.getColor(itemView.getContext(), R.color.on_surface));
|
||||
}
|
||||
|
||||
// Priority dot color
|
||||
int priorityColor;
|
||||
switch (todo.getPriority()) {
|
||||
case "High":
|
||||
priorityColor = ContextCompat.getColor(itemView.getContext(), R.color.priority_high);
|
||||
break;
|
||||
case "Medium":
|
||||
priorityColor = ContextCompat.getColor(itemView.getContext(), R.color.priority_medium);
|
||||
break;
|
||||
default:
|
||||
priorityColor = ContextCompat.getColor(itemView.getContext(), R.color.priority_low);
|
||||
break;
|
||||
}
|
||||
GradientDrawable dot = (GradientDrawable) binding.priorityDot.getBackground().mutate();
|
||||
dot.setColor(priorityColor);
|
||||
|
||||
// Click listener for editing
|
||||
itemView.setOnClickListener(v -> {
|
||||
if (clickListener != null) {
|
||||
clickListener.onTodoClick(todo);
|
||||
}
|
||||
});
|
||||
|
||||
// Checkbox listener
|
||||
binding.checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
||||
if (checkedListener != null) {
|
||||
checkedListener.onTodoChecked(todo);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package com.example.todojava;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.example.todojava.data.Todo;
|
||||
import com.example.todojava.databinding.DialogTodoFormBinding;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
public class TodoFormDialogFragment extends DialogFragment {
|
||||
|
||||
private static final String ARG_TODO_ID = "todo_id";
|
||||
private static final String ARG_TODO_TITLE = "todo_title";
|
||||
private static final String ARG_TODO_CATEGORY = "todo_category";
|
||||
private static final String ARG_TODO_PRIORITY = "todo_priority";
|
||||
private static final String ARG_TODO_COMPLETED = "todo_completed";
|
||||
private static final String ARG_TODO_CREATED_AT = "todo_created_at";
|
||||
|
||||
private DialogTodoFormBinding binding;
|
||||
private TodoViewModel viewModel;
|
||||
|
||||
public static TodoFormDialogFragment newInstance() {
|
||||
return new TodoFormDialogFragment();
|
||||
}
|
||||
|
||||
public static TodoFormDialogFragment newInstance(Todo todo) {
|
||||
TodoFormDialogFragment fragment = new TodoFormDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putLong(ARG_TODO_ID, todo.getId());
|
||||
args.putString(ARG_TODO_TITLE, todo.getTitle());
|
||||
args.putString(ARG_TODO_CATEGORY, todo.getCategory());
|
||||
args.putString(ARG_TODO_PRIORITY, todo.getPriority());
|
||||
args.putBoolean(ARG_TODO_COMPLETED, todo.isCompleted());
|
||||
args.putLong(ARG_TODO_CREATED_AT, todo.getCreatedAt());
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
private boolean isEditing() {
|
||||
return getArguments() != null && getArguments().containsKey(ARG_TODO_ID);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
binding = DialogTodoFormBinding.inflate(LayoutInflater.from(getContext()));
|
||||
viewModel = new ViewModelProvider(requireActivity()).get(TodoViewModel.class);
|
||||
|
||||
if (isEditing()) {
|
||||
prefillForm();
|
||||
}
|
||||
|
||||
String title = isEditing() ? getString(R.string.edit_todo) : getString(R.string.add_todo);
|
||||
|
||||
return new MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(title)
|
||||
.setView(binding.getRoot())
|
||||
.setPositiveButton(R.string.save, (dialog, which) -> saveTodo())
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.create();
|
||||
}
|
||||
|
||||
private void prefillForm() {
|
||||
Bundle args = getArguments();
|
||||
if (args == null) return;
|
||||
|
||||
binding.editTitle.setText(args.getString(ARG_TODO_TITLE, ""));
|
||||
|
||||
String category = args.getString(ARG_TODO_CATEGORY, "Personal");
|
||||
switch (category) {
|
||||
case "Personal":
|
||||
binding.chipPersonal.setChecked(true);
|
||||
break;
|
||||
case "Work":
|
||||
binding.chipWork.setChecked(true);
|
||||
break;
|
||||
case "Shopping":
|
||||
binding.chipShopping.setChecked(true);
|
||||
break;
|
||||
case "Health":
|
||||
binding.chipHealth.setChecked(true);
|
||||
break;
|
||||
case "Learning":
|
||||
binding.chipLearning.setChecked(true);
|
||||
break;
|
||||
}
|
||||
|
||||
String priority = args.getString(ARG_TODO_PRIORITY, "Medium");
|
||||
switch (priority) {
|
||||
case "Low":
|
||||
binding.chipLow.setChecked(true);
|
||||
break;
|
||||
case "Medium":
|
||||
binding.chipMedium.setChecked(true);
|
||||
break;
|
||||
case "High":
|
||||
binding.chipHigh.setChecked(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private String getSelectedCategory() {
|
||||
int checkedId = binding.categoryChipGroup.getCheckedChipId();
|
||||
if (checkedId == R.id.chipPersonal) return "Personal";
|
||||
if (checkedId == R.id.chipWork) return "Work";
|
||||
if (checkedId == R.id.chipShopping) return "Shopping";
|
||||
if (checkedId == R.id.chipHealth) return "Health";
|
||||
if (checkedId == R.id.chipLearning) return "Learning";
|
||||
return "Personal";
|
||||
}
|
||||
|
||||
private String getSelectedPriority() {
|
||||
int checkedId = binding.priorityChipGroup.getCheckedChipId();
|
||||
if (checkedId == R.id.chipLow) return "Low";
|
||||
if (checkedId == R.id.chipMedium) return "Medium";
|
||||
if (checkedId == R.id.chipHigh) return "High";
|
||||
return "Medium";
|
||||
}
|
||||
|
||||
private void saveTodo() {
|
||||
String title = binding.editTitle.getText() != null
|
||||
? binding.editTitle.getText().toString().trim() : "";
|
||||
|
||||
if (title.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
String category = getSelectedCategory();
|
||||
String priority = getSelectedPriority();
|
||||
|
||||
if (isEditing()) {
|
||||
Bundle args = getArguments();
|
||||
Todo todo = new Todo(title, category, priority);
|
||||
todo.setId(args.getLong(ARG_TODO_ID));
|
||||
todo.setCompleted(args.getBoolean(ARG_TODO_COMPLETED, false));
|
||||
todo.setCreatedAt(args.getLong(ARG_TODO_CREATED_AT, System.currentTimeMillis()));
|
||||
viewModel.update(todo);
|
||||
} else {
|
||||
Todo todo = new Todo(title, category, priority);
|
||||
viewModel.insert(todo);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package com.example.todojava;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.AndroidViewModel;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MediatorLiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.example.todojava.data.Todo;
|
||||
import com.example.todojava.data.TodoDao;
|
||||
import com.example.todojava.data.TodoDatabase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class TodoViewModel extends AndroidViewModel {
|
||||
|
||||
public enum Filter {
|
||||
ALL, ACTIVE, DONE
|
||||
}
|
||||
|
||||
private final TodoDao todoDao;
|
||||
private final ExecutorService executor;
|
||||
|
||||
private final LiveData<List<Todo>> allTodos;
|
||||
private final LiveData<List<Todo>> activeTodos;
|
||||
private final LiveData<List<Todo>> completedTodos;
|
||||
|
||||
private final LiveData<Integer> allCount;
|
||||
private final LiveData<Integer> activeCount;
|
||||
private final LiveData<Integer> completedCount;
|
||||
|
||||
private final MutableLiveData<Filter> currentFilter = new MutableLiveData<>(Filter.ALL);
|
||||
private final MediatorLiveData<List<Todo>> filteredTodos = new MediatorLiveData<>();
|
||||
|
||||
public TodoViewModel(@NonNull Application application) {
|
||||
super(application);
|
||||
TodoDatabase db = TodoDatabase.getInstance(application);
|
||||
todoDao = db.todoDao();
|
||||
executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
allTodos = todoDao.getAllTodos();
|
||||
activeTodos = todoDao.getActiveTodos();
|
||||
completedTodos = todoDao.getCompletedTodos();
|
||||
|
||||
allCount = todoDao.getAllCount();
|
||||
activeCount = todoDao.getActiveCount();
|
||||
completedCount = todoDao.getCompletedCount();
|
||||
|
||||
filteredTodos.addSource(allTodos, todos -> {
|
||||
if (currentFilter.getValue() == Filter.ALL) {
|
||||
filteredTodos.setValue(todos);
|
||||
}
|
||||
});
|
||||
filteredTodos.addSource(activeTodos, todos -> {
|
||||
if (currentFilter.getValue() == Filter.ACTIVE) {
|
||||
filteredTodos.setValue(todos);
|
||||
}
|
||||
});
|
||||
filteredTodos.addSource(completedTodos, todos -> {
|
||||
if (currentFilter.getValue() == Filter.DONE) {
|
||||
filteredTodos.setValue(todos);
|
||||
}
|
||||
});
|
||||
filteredTodos.addSource(currentFilter, filter -> {
|
||||
switch (filter) {
|
||||
case ALL:
|
||||
filteredTodos.setValue(allTodos.getValue());
|
||||
break;
|
||||
case ACTIVE:
|
||||
filteredTodos.setValue(activeTodos.getValue());
|
||||
break;
|
||||
case DONE:
|
||||
filteredTodos.setValue(completedTodos.getValue());
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public LiveData<List<Todo>> getFilteredTodos() {
|
||||
return filteredTodos;
|
||||
}
|
||||
|
||||
public LiveData<Integer> getAllCount() {
|
||||
return allCount;
|
||||
}
|
||||
|
||||
public LiveData<Integer> getActiveCount() {
|
||||
return activeCount;
|
||||
}
|
||||
|
||||
public LiveData<Integer> getCompletedCount() {
|
||||
return completedCount;
|
||||
}
|
||||
|
||||
public MutableLiveData<Filter> getCurrentFilter() {
|
||||
return currentFilter;
|
||||
}
|
||||
|
||||
public void setFilter(Filter filter) {
|
||||
currentFilter.setValue(filter);
|
||||
}
|
||||
|
||||
public void insert(Todo todo) {
|
||||
executor.execute(() -> todoDao.insert(todo));
|
||||
}
|
||||
|
||||
public void update(Todo todo) {
|
||||
executor.execute(() -> todoDao.update(todo));
|
||||
}
|
||||
|
||||
public void delete(Todo todo) {
|
||||
executor.execute(() -> todoDao.delete(todo));
|
||||
}
|
||||
|
||||
public void toggleCompleted(Todo todo) {
|
||||
todo.setCompleted(!todo.isCompleted());
|
||||
executor.execute(() -> todoDao.update(todo));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.example.todojava.data;
|
||||
|
||||
import androidx.room.ColumnInfo;
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
@Entity(tableName = "todos")
|
||||
public class Todo {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
public long id;
|
||||
|
||||
@ColumnInfo(name = "title")
|
||||
public String title;
|
||||
|
||||
@ColumnInfo(name = "completed")
|
||||
public boolean completed;
|
||||
|
||||
@ColumnInfo(name = "category")
|
||||
public String category;
|
||||
|
||||
@ColumnInfo(name = "priority")
|
||||
public String priority;
|
||||
|
||||
@ColumnInfo(name = "created_at")
|
||||
public long createdAt;
|
||||
|
||||
public Todo() {
|
||||
}
|
||||
|
||||
@Ignore
|
||||
public Todo(String title, String category, String priority) {
|
||||
this.title = title;
|
||||
this.completed = false;
|
||||
this.category = category;
|
||||
this.priority = priority;
|
||||
this.createdAt = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public boolean isCompleted() {
|
||||
return completed;
|
||||
}
|
||||
|
||||
public void setCompleted(boolean completed) {
|
||||
this.completed = completed;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
public void setPriority(String priority) {
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
public long getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(long createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.example.todojava.data;
|
||||
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.room.Dao;
|
||||
import androidx.room.Delete;
|
||||
import androidx.room.Insert;
|
||||
import androidx.room.Query;
|
||||
import androidx.room.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface TodoDao {
|
||||
|
||||
@Query("SELECT * FROM todos ORDER BY created_at DESC")
|
||||
LiveData<List<Todo>> getAllTodos();
|
||||
|
||||
@Query("SELECT * FROM todos WHERE completed = 0 ORDER BY created_at DESC")
|
||||
LiveData<List<Todo>> getActiveTodos();
|
||||
|
||||
@Query("SELECT * FROM todos WHERE completed = 1 ORDER BY created_at DESC")
|
||||
LiveData<List<Todo>> getCompletedTodos();
|
||||
|
||||
@Query("SELECT COUNT(*) FROM todos")
|
||||
LiveData<Integer> getAllCount();
|
||||
|
||||
@Query("SELECT COUNT(*) FROM todos WHERE completed = 0")
|
||||
LiveData<Integer> getActiveCount();
|
||||
|
||||
@Query("SELECT COUNT(*) FROM todos WHERE completed = 1")
|
||||
LiveData<Integer> getCompletedCount();
|
||||
|
||||
@Insert
|
||||
void insert(Todo todo);
|
||||
|
||||
@Update
|
||||
void update(Todo todo);
|
||||
|
||||
@Delete
|
||||
void delete(Todo todo);
|
||||
|
||||
@Query("SELECT * FROM todos WHERE id = :id")
|
||||
Todo getTodoById(long id);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.example.todojava.data;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.room.Database;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
|
||||
@Database(entities = {Todo.class}, version = 1, exportSchema = false)
|
||||
public abstract class TodoDatabase extends RoomDatabase {
|
||||
|
||||
public abstract TodoDao todoDao();
|
||||
|
||||
private static volatile TodoDatabase INSTANCE;
|
||||
|
||||
public static TodoDatabase getInstance(Context context) {
|
||||
if (INSTANCE == null) {
|
||||
synchronized (TodoDatabase.class) {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = Room.databaseBuilder(
|
||||
context.getApplicationContext(),
|
||||
TodoDatabase.class,
|
||||
"todo_database"
|
||||
).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="oval">
|
||||
<solid android:color="#43A047" />
|
||||
<size
|
||||
android:width="12dp"
|
||||
android:height="12dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,103 @@
|
||||
<?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"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.appbar.MaterialToolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/primary"
|
||||
app:title="@string/toolbar_title"
|
||||
app:titleTextColor="@color/on_primary" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/filterChipGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="12dp"
|
||||
app:singleSelection="true"
|
||||
app:selectionRequired="true"
|
||||
app:chipSpacingHorizontal="8dp">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipAll"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/filter_all"
|
||||
android:checked="true" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipActive"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/filter_active" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipDone"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/filter_done" />
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/divider" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:padding="8dp"
|
||||
tools:listitem="@layout/item_todo" />
|
||||
|
||||
<include
|
||||
android:id="@+id/emptyState"
|
||||
layout="@layout/empty_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="16dp"
|
||||
android:contentDescription="@string/add_todo"
|
||||
app:srcCompat="@android:drawable/ic_input_add"
|
||||
app:tint="@color/on_primary"
|
||||
app:backgroundTint="@color/primary" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -0,0 +1,130 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView
|
||||
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="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/titleInputLayout"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/title_hint">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/editTitle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textCapSentences"
|
||||
android:maxLines="2" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_label"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/on_surface"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/categoryChipGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:singleSelection="true"
|
||||
app:selectionRequired="true"
|
||||
app:chipSpacingHorizontal="8dp">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipPersonal"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_personal"
|
||||
android:checked="true" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipWork"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_work" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipShopping"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_shopping" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipHealth"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_health" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipLearning"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/category_learning" />
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/priority_label"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/on_surface"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/priorityChipGroup"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:singleSelection="true"
|
||||
app:selectionRequired="true"
|
||||
app:chipSpacingHorizontal="8dp">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipLow"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/priority_low" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipMedium"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/priority_medium"
|
||||
android:checked="true" />
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipHigh"
|
||||
style="@style/Widget.Material3.Chip.Filter"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/priority_high" />
|
||||
|
||||
</com.google.android.material.chip.ChipGroup>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:padding="32dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:src="@android:drawable/ic_menu_agenda"
|
||||
android:alpha="0.4"
|
||||
android:importantForAccessibility="no" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/emptyTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/empty_state_title"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/on_surface"
|
||||
android:layout_marginTop="16dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/emptySubtitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/empty_state_subtitle"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/on_surface_variant"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="center" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="4dp"
|
||||
android:layout_marginVertical="4dp"
|
||||
app:cardElevation="2dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="12dp">
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/checkBox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:minWidth="0dp"
|
||||
android:minHeight="0dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:layout_marginStart="8dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textTitle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="16sp"
|
||||
android:textColor="@color/on_surface"
|
||||
tools:text="Buy groceries" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginTop="4dp">
|
||||
|
||||
<com.google.android.material.chip.Chip
|
||||
android:id="@+id/chipCategory"
|
||||
style="@style/Widget.Material3.Chip.Assist"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="11sp"
|
||||
app:chipMinHeight="24dp"
|
||||
app:chipMinTouchTargetSize="0dp"
|
||||
tools:text="Shopping" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:id="@+id/priorityDot"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/priority_dot" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 164 B |
Binary file not shown.
|
After Width: | Height: | Size: 122 B |
Binary file not shown.
|
After Width: | Height: | Size: 220 B |
Binary file not shown.
|
After Width: | Height: | Size: 413 B |
Binary file not shown.
|
After Width: | Height: | Size: 546 B |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="primary">#6200EE</color>
|
||||
<color name="primary_variant">#3700B3</color>
|
||||
<color name="on_primary">#FFFFFF</color>
|
||||
<color name="secondary">#03DAC6</color>
|
||||
<color name="on_secondary">#000000</color>
|
||||
<color name="background">#FAFAFA</color>
|
||||
<color name="surface">#FFFFFF</color>
|
||||
<color name="on_surface">#1C1B1F</color>
|
||||
<color name="on_surface_variant">#49454F</color>
|
||||
|
||||
<color name="priority_low">#43A047</color>
|
||||
<color name="priority_medium">#FB8C00</color>
|
||||
<color name="priority_high">#E53935</color>
|
||||
|
||||
<color name="completed_text">#9E9E9E</color>
|
||||
<color name="divider">#E0E0E0</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Todo Java</string>
|
||||
<string name="toolbar_title">Todos \u2013 Java Android</string>
|
||||
<string name="filter_all">All</string>
|
||||
<string name="filter_active">Active</string>
|
||||
<string name="filter_done">Done</string>
|
||||
<string name="add_todo">Add Todo</string>
|
||||
<string name="edit_todo">Edit Todo</string>
|
||||
<string name="title_hint">What needs to be done?</string>
|
||||
<string name="category_label">Category</string>
|
||||
<string name="priority_label">Priority</string>
|
||||
<string name="save">Save</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="empty_state_title">No todos yet</string>
|
||||
<string name="empty_state_subtitle">Tap the + button to add your first todo</string>
|
||||
<string name="empty_active_title">All done!</string>
|
||||
<string name="empty_active_subtitle">No active todos remaining</string>
|
||||
<string name="empty_done_title">Nothing completed</string>
|
||||
<string name="empty_done_subtitle">Complete a todo to see it here</string>
|
||||
<string name="category_personal">Personal</string>
|
||||
<string name="category_work">Work</string>
|
||||
<string name="category_shopping">Shopping</string>
|
||||
<string name="category_health">Health</string>
|
||||
<string name="category_learning">Learning</string>
|
||||
<string name="priority_low">Low</string>
|
||||
<string name="priority_medium">Medium</string>
|
||||
<string name="priority_high">High</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="todo_deleted">Todo deleted</string>
|
||||
<string name="undo">Undo</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<style name="Theme.TodoJava" parent="Theme.Material3.Light.NoActionBar">
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryVariant">@color/primary_variant</item>
|
||||
<item name="colorOnPrimary">@color/on_primary</item>
|
||||
<item name="colorSecondary">@color/secondary</item>
|
||||
<item name="colorOnSecondary">@color/on_secondary</item>
|
||||
<item name="android:colorBackground">@color/background</item>
|
||||
<item name="colorSurface">@color/surface</item>
|
||||
<item name="colorOnSurface">@color/on_surface</item>
|
||||
<item name="colorOnSurfaceVariant">@color/on_surface_variant</item>
|
||||
</style>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user