├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── example │ │ └── android │ │ └── todolist │ │ ├── AddTaskActivity.java │ │ ├── AddTaskViewModel.java │ │ ├── AddTaskViewModelFactory.java │ │ ├── AppExecutors.java │ │ ├── MainActivity.java │ │ ├── MainViewModel.java │ │ ├── TaskAdapter.java │ │ └── database │ │ ├── AppDatabase.java │ │ ├── DateConverter.java │ │ ├── TaskDao.java │ │ └── TaskEntry.java │ └── res │ ├── drawable │ └── priority_circle.xml │ ├── layout │ ├── activity_add_task.xml │ ├── activity_main.xml │ └── task_layout.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Archival Note 3 | This repository is deprecated; therefore, we are going to archive it. However, learners will be able to fork it to their personal Github account but cannot submit PRs to this repository. If you have any issues or suggestions to make, feel free to: 4 | - Utilize the https://knowledge.udacity.com/ forum to seek help on content-specific issues. 5 | - Submit a support ticket along with the link to your forked repository if (learners are) blocked for other reasons. Here are the links for the [retail consumers](https://udacity.zendesk.com/hc/en-us/requests/new) and [enterprise learners](https://udacityenterprise.zendesk.com/hc/en-us/requests/new?ticket_form_id=360000279131). -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | buildToolsVersion '27.0.1' 6 | defaultConfig { 7 | applicationId "com.example.android.todolist" 8 | minSdkVersion 15 9 | targetSdkVersion 27 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | compile fileTree(dir: 'libs', include: ['*.jar']) 23 | compile 'com.android.support:appcompat-v7:27.0.2' 24 | 25 | //add Recycler view dependencies; must match SDK version 26 | compile 'com.android.support:recyclerview-v7:27.0.2' 27 | 28 | //FAB dependencies 29 | compile 'com.android.support:design:27.0.2' 30 | 31 | compile "android.arch.persistence.room:runtime:1.0.0" 32 | annotationProcessor "android.arch.persistence.room:compiler:1.0.0" 33 | 34 | implementation "android.arch.lifecycle:extensions:1.1.0" 35 | annotationProcessor "android.arch.lifecycle:compiler:1.1.0" 36 | 37 | //Testing 38 | // Instrumentation dependencies use androidTestCompile 39 | // (as opposed to testCompile for local unit tests run in the JVM) 40 | androidTestCompile 'junit:junit:4.12' 41 | androidTestCompile 'com.android.support:support-annotations:27.0.2' 42 | androidTestCompile 'com.android.support.test:runner:1.0.1' 43 | androidTestCompile 'com.android.support.test:rules:1.0.1' 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/AddTaskActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.android.todolist; 18 | 19 | import android.arch.lifecycle.Observer; 20 | import android.arch.lifecycle.ViewModelProviders; 21 | import android.content.Intent; 22 | import android.os.Bundle; 23 | import android.support.annotation.Nullable; 24 | import android.support.v7.app.AppCompatActivity; 25 | import android.view.View; 26 | import android.widget.Button; 27 | import android.widget.EditText; 28 | import android.widget.RadioGroup; 29 | 30 | import com.example.android.todolist.database.AppDatabase; 31 | import com.example.android.todolist.database.TaskEntry; 32 | 33 | import java.util.Date; 34 | 35 | 36 | public class AddTaskActivity extends AppCompatActivity { 37 | 38 | // Extra for the task ID to be received in the intent 39 | public static final String EXTRA_TASK_ID = "extraTaskId"; 40 | // Extra for the task ID to be received after rotation 41 | public static final String INSTANCE_TASK_ID = "instanceTaskId"; 42 | // Constants for priority 43 | public static final int PRIORITY_HIGH = 1; 44 | public static final int PRIORITY_MEDIUM = 2; 45 | public static final int PRIORITY_LOW = 3; 46 | // Constant for default task id to be used when not in update mode 47 | private static final int DEFAULT_TASK_ID = -1; 48 | // Constant for logging 49 | private static final String TAG = AddTaskActivity.class.getSimpleName(); 50 | // Fields for views 51 | EditText mEditText; 52 | RadioGroup mRadioGroup; 53 | Button mButton; 54 | 55 | private int mTaskId = DEFAULT_TASK_ID; 56 | 57 | // Member variable for the Database 58 | private AppDatabase mDb; 59 | 60 | protected void onCreate(Bundle savedInstanceState) { 61 | super.onCreate(savedInstanceState); 62 | setContentView(R.layout.activity_add_task); 63 | 64 | initViews(); 65 | 66 | mDb = AppDatabase.getInstance(getApplicationContext()); 67 | 68 | if (savedInstanceState != null && savedInstanceState.containsKey(INSTANCE_TASK_ID)) { 69 | mTaskId = savedInstanceState.getInt(INSTANCE_TASK_ID, DEFAULT_TASK_ID); 70 | } 71 | 72 | Intent intent = getIntent(); 73 | if (intent != null && intent.hasExtra(EXTRA_TASK_ID)) { 74 | mButton.setText(R.string.update_button); 75 | if (mTaskId == DEFAULT_TASK_ID) { 76 | // populate the UI 77 | mTaskId = intent.getIntExtra(EXTRA_TASK_ID, DEFAULT_TASK_ID); 78 | 79 | // COMPLETED (9) Remove the logging and the call to loadTaskById, this is done in the ViewModel now 80 | // COMPLETED (10) Declare a AddTaskViewModelFactory using mDb and mTaskId 81 | AddTaskViewModelFactory factory = new AddTaskViewModelFactory(mDb, mTaskId); 82 | // COMPLETED (11) Declare a AddTaskViewModel variable and initialize it by calling ViewModelProviders.of 83 | // for that use the factory created above AddTaskViewModel 84 | final AddTaskViewModel viewModel 85 | = ViewModelProviders.of(this, factory).get(AddTaskViewModel.class); 86 | 87 | // COMPLETED (12) Observe the LiveData object in the ViewModel. Use it also when removing the observer 88 | viewModel.getTask().observe(this, new Observer() { 89 | @Override 90 | public void onChanged(@Nullable TaskEntry taskEntry) { 91 | viewModel.getTask().removeObserver(this); 92 | populateUI(taskEntry); 93 | } 94 | }); 95 | } 96 | } 97 | } 98 | 99 | @Override 100 | protected void onSaveInstanceState(Bundle outState) { 101 | outState.putInt(INSTANCE_TASK_ID, mTaskId); 102 | super.onSaveInstanceState(outState); 103 | } 104 | 105 | /** 106 | * initViews is called from onCreate to init the member variable views 107 | */ 108 | private void initViews() { 109 | mEditText = findViewById(R.id.editTextTaskDescription); 110 | mRadioGroup = findViewById(R.id.radioGroup); 111 | 112 | mButton = findViewById(R.id.saveButton); 113 | mButton.setOnClickListener(new View.OnClickListener() { 114 | @Override 115 | public void onClick(View view) { 116 | onSaveButtonClicked(); 117 | } 118 | }); 119 | } 120 | 121 | /** 122 | * populateUI would be called to populate the UI when in update mode 123 | * 124 | * @param task the taskEntry to populate the UI 125 | */ 126 | private void populateUI(TaskEntry task) { 127 | if (task == null) { 128 | return; 129 | } 130 | 131 | mEditText.setText(task.getDescription()); 132 | setPriorityInViews(task.getPriority()); 133 | } 134 | 135 | /** 136 | * onSaveButtonClicked is called when the "save" button is clicked. 137 | * It retrieves user input and inserts that new task data into the underlying database. 138 | */ 139 | public void onSaveButtonClicked() { 140 | String description = mEditText.getText().toString(); 141 | int priority = getPriorityFromViews(); 142 | Date date = new Date(); 143 | 144 | final TaskEntry task = new TaskEntry(description, priority, date); 145 | AppExecutors.getInstance().diskIO().execute(new Runnable() { 146 | @Override 147 | public void run() { 148 | if (mTaskId == DEFAULT_TASK_ID) { 149 | // insert new task 150 | mDb.taskDao().insertTask(task); 151 | } else { 152 | //update task 153 | task.setId(mTaskId); 154 | mDb.taskDao().updateTask(task); 155 | } 156 | finish(); 157 | } 158 | }); 159 | } 160 | 161 | /** 162 | * getPriority is called whenever the selected priority needs to be retrieved 163 | */ 164 | public int getPriorityFromViews() { 165 | int priority = 1; 166 | int checkedId = ((RadioGroup) findViewById(R.id.radioGroup)).getCheckedRadioButtonId(); 167 | switch (checkedId) { 168 | case R.id.radButton1: 169 | priority = PRIORITY_HIGH; 170 | break; 171 | case R.id.radButton2: 172 | priority = PRIORITY_MEDIUM; 173 | break; 174 | case R.id.radButton3: 175 | priority = PRIORITY_LOW; 176 | } 177 | return priority; 178 | } 179 | 180 | /** 181 | * setPriority is called when we receive a task from MainActivity 182 | * 183 | * @param priority the priority value 184 | */ 185 | public void setPriorityInViews(int priority) { 186 | switch (priority) { 187 | case PRIORITY_HIGH: 188 | ((RadioGroup) findViewById(R.id.radioGroup)).check(R.id.radButton1); 189 | break; 190 | case PRIORITY_MEDIUM: 191 | ((RadioGroup) findViewById(R.id.radioGroup)).check(R.id.radButton2); 192 | break; 193 | case PRIORITY_LOW: 194 | ((RadioGroup) findViewById(R.id.radioGroup)).check(R.id.radButton3); 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/AddTaskViewModel.java: -------------------------------------------------------------------------------- 1 | package com.example.android.todolist; 2 | 3 | import android.arch.lifecycle.LiveData; 4 | import android.arch.lifecycle.ViewModel; 5 | 6 | import com.example.android.todolist.database.AppDatabase; 7 | import com.example.android.todolist.database.TaskEntry; 8 | 9 | // COMPLETED (5) Make this class extend ViewModel 10 | public class AddTaskViewModel extends ViewModel { 11 | 12 | // COMPLETED (6) Add a task member variable for the TaskEntry object wrapped in a LiveData 13 | private LiveData task; 14 | 15 | // COMPLETED (8) Create a constructor where you call loadTaskById of the taskDao to initialize the tasks variable 16 | // Note: The constructor should receive the database and the taskId 17 | public AddTaskViewModel(AppDatabase database, int taskId) { 18 | task = database.taskDao().loadTaskById(taskId); 19 | } 20 | 21 | // COMPLETED (7) Create a getter for the task variable 22 | public LiveData getTask() { 23 | return task; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/AddTaskViewModelFactory.java: -------------------------------------------------------------------------------- 1 | package com.example.android.todolist; 2 | 3 | import android.arch.lifecycle.ViewModel; 4 | import android.arch.lifecycle.ViewModelProvider; 5 | 6 | import com.example.android.todolist.database.AppDatabase; 7 | 8 | // COMPLETED (1) Make this class extend ViewModel ViewModelProvider.NewInstanceFactory 9 | public class AddTaskViewModelFactory extends ViewModelProvider.NewInstanceFactory { 10 | 11 | // COMPLETED (2) Add two member variables. One for the database and one for the taskId 12 | private final AppDatabase mDb; 13 | private final int mTaskId; 14 | 15 | // COMPLETED (3) Initialize the member variables in the constructor with the parameters received 16 | public AddTaskViewModelFactory(AppDatabase database, int taskId) { 17 | mDb = database; 18 | mTaskId = taskId; 19 | } 20 | 21 | // COMPLETED (4) Uncomment the following method 22 | @Override 23 | public T create(Class modelClass) { 24 | //noinspection unchecked 25 | return (T) new AddTaskViewModel(mDb, mTaskId); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/AppExecutors.java: -------------------------------------------------------------------------------- 1 | package com.example.android.todolist; 2 | 3 | /* 4 | * Copyright (C) 2017 The Android Open Source Project 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | import android.os.Handler; 20 | import android.os.Looper; 21 | import android.support.annotation.NonNull; 22 | 23 | import java.util.concurrent.Executor; 24 | import java.util.concurrent.Executors; 25 | 26 | /** 27 | * Global executor pools for the whole application. 28 | *

29 | * Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind 30 | * webservice requests). 31 | */ 32 | public class AppExecutors { 33 | 34 | // For Singleton instantiation 35 | private static final Object LOCK = new Object(); 36 | private static AppExecutors sInstance; 37 | private final Executor diskIO; 38 | private final Executor mainThread; 39 | private final Executor networkIO; 40 | 41 | private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) { 42 | this.diskIO = diskIO; 43 | this.networkIO = networkIO; 44 | this.mainThread = mainThread; 45 | } 46 | 47 | public static AppExecutors getInstance() { 48 | if (sInstance == null) { 49 | synchronized (LOCK) { 50 | sInstance = new AppExecutors(Executors.newSingleThreadExecutor(), 51 | Executors.newFixedThreadPool(3), 52 | new MainThreadExecutor()); 53 | } 54 | } 55 | return sInstance; 56 | } 57 | 58 | public Executor diskIO() { 59 | return diskIO; 60 | } 61 | 62 | public Executor mainThread() { 63 | return mainThread; 64 | } 65 | 66 | public Executor networkIO() { 67 | return networkIO; 68 | } 69 | 70 | private static class MainThreadExecutor implements Executor { 71 | private Handler mainThreadHandler = new Handler(Looper.getMainLooper()); 72 | 73 | @Override 74 | public void execute(@NonNull Runnable command) { 75 | mainThreadHandler.post(command); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.android.todolist; 18 | 19 | import android.arch.lifecycle.Observer; 20 | import android.arch.lifecycle.ViewModelProviders; 21 | import android.content.Intent; 22 | import android.os.Bundle; 23 | import android.support.annotation.Nullable; 24 | import android.support.design.widget.FloatingActionButton; 25 | import android.support.v7.app.AppCompatActivity; 26 | import android.support.v7.widget.DividerItemDecoration; 27 | import android.support.v7.widget.LinearLayoutManager; 28 | import android.support.v7.widget.RecyclerView; 29 | import android.support.v7.widget.helper.ItemTouchHelper; 30 | import android.util.Log; 31 | import android.view.View; 32 | 33 | import com.example.android.todolist.database.AppDatabase; 34 | import com.example.android.todolist.database.TaskEntry; 35 | 36 | import java.util.List; 37 | 38 | import static android.support.v7.widget.DividerItemDecoration.VERTICAL; 39 | 40 | 41 | public class MainActivity extends AppCompatActivity implements TaskAdapter.ItemClickListener { 42 | 43 | // Constant for logging 44 | private static final String TAG = MainActivity.class.getSimpleName(); 45 | // Member variables for the adapter and RecyclerView 46 | private RecyclerView mRecyclerView; 47 | private TaskAdapter mAdapter; 48 | 49 | private AppDatabase mDb; 50 | 51 | @Override 52 | protected void onCreate(Bundle savedInstanceState) { 53 | super.onCreate(savedInstanceState); 54 | setContentView(R.layout.activity_main); 55 | 56 | // Set the RecyclerView to its corresponding view 57 | mRecyclerView = findViewById(R.id.recyclerViewTasks); 58 | 59 | // Set the layout for the RecyclerView to be a linear layout, which measures and 60 | // positions items within a RecyclerView into a linear list 61 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 62 | 63 | // Initialize the adapter and attach it to the RecyclerView 64 | mAdapter = new TaskAdapter(this, this); 65 | mRecyclerView.setAdapter(mAdapter); 66 | 67 | DividerItemDecoration decoration = new DividerItemDecoration(getApplicationContext(), VERTICAL); 68 | mRecyclerView.addItemDecoration(decoration); 69 | 70 | /* 71 | Add a touch helper to the RecyclerView to recognize when a user swipes to delete an item. 72 | An ItemTouchHelper enables touch behavior (like swipe and move) on each ViewHolder, 73 | and uses callbacks to signal when a user is performing these actions. 74 | */ 75 | new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) { 76 | @Override 77 | public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 78 | return false; 79 | } 80 | 81 | // Called when a user swipes left or right on a ViewHolder 82 | @Override 83 | public void onSwiped(final RecyclerView.ViewHolder viewHolder, int swipeDir) { 84 | // Here is where you'll implement swipe to delete 85 | AppExecutors.getInstance().diskIO().execute(new Runnable() { 86 | @Override 87 | public void run() { 88 | int position = viewHolder.getAdapterPosition(); 89 | List tasks = mAdapter.getTasks(); 90 | mDb.taskDao().deleteTask(tasks.get(position)); 91 | } 92 | }); 93 | } 94 | }).attachToRecyclerView(mRecyclerView); 95 | 96 | /* 97 | Set the Floating Action Button (FAB) to its corresponding View. 98 | Attach an OnClickListener to it, so that when it's clicked, a new intent will be created 99 | to launch the AddTaskActivity. 100 | */ 101 | FloatingActionButton fabButton = findViewById(R.id.fab); 102 | 103 | fabButton.setOnClickListener(new View.OnClickListener() { 104 | @Override 105 | public void onClick(View view) { 106 | // Create a new intent to start an AddTaskActivity 107 | Intent addTaskIntent = new Intent(MainActivity.this, AddTaskActivity.class); 108 | startActivity(addTaskIntent); 109 | } 110 | }); 111 | 112 | mDb = AppDatabase.getInstance(getApplicationContext()); 113 | setupViewModel(); 114 | } 115 | 116 | private void setupViewModel() { 117 | MainViewModel viewModel = ViewModelProviders.of(this).get(MainViewModel.class); 118 | viewModel.getTasks().observe(this, new Observer>() { 119 | @Override 120 | public void onChanged(@Nullable List taskEntries) { 121 | Log.d(TAG, "Updating list of tasks from LiveData in ViewModel"); 122 | mAdapter.setTasks(taskEntries); 123 | } 124 | }); 125 | } 126 | 127 | @Override 128 | public void onItemClickListener(int itemId) { 129 | // Launch AddTaskActivity adding the itemId as an extra in the intent 130 | Intent intent = new Intent(MainActivity.this, AddTaskActivity.class); 131 | intent.putExtra(AddTaskActivity.EXTRA_TASK_ID, itemId); 132 | startActivity(intent); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/MainViewModel.java: -------------------------------------------------------------------------------- 1 | package com.example.android.todolist; 2 | 3 | import android.app.Application; 4 | import android.arch.lifecycle.AndroidViewModel; 5 | import android.arch.lifecycle.LiveData; 6 | import android.util.Log; 7 | 8 | import com.example.android.todolist.database.AppDatabase; 9 | import com.example.android.todolist.database.TaskEntry; 10 | 11 | import java.util.List; 12 | 13 | public class MainViewModel extends AndroidViewModel { 14 | 15 | // Constant for logging 16 | private static final String TAG = MainViewModel.class.getSimpleName(); 17 | 18 | private LiveData> tasks; 19 | 20 | public MainViewModel(Application application) { 21 | super(application); 22 | AppDatabase database = AppDatabase.getInstance(this.getApplication()); 23 | Log.d(TAG, "Actively retrieving the tasks from the DataBase"); 24 | tasks = database.taskDao().loadAllTasks(); 25 | } 26 | 27 | public LiveData> getTasks() { 28 | return tasks; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/TaskAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.example.android.todolist; 18 | 19 | import android.content.Context; 20 | import android.graphics.drawable.GradientDrawable; 21 | import android.support.v4.content.ContextCompat; 22 | import android.support.v7.widget.RecyclerView; 23 | import android.view.LayoutInflater; 24 | import android.view.View; 25 | import android.view.ViewGroup; 26 | import android.widget.TextView; 27 | 28 | import com.example.android.todolist.database.TaskEntry; 29 | 30 | import java.text.SimpleDateFormat; 31 | import java.util.List; 32 | import java.util.Locale; 33 | 34 | /** 35 | * This TaskAdapter creates and binds ViewHolders, that hold the description and priority of a task, 36 | * to a RecyclerView to efficiently display data. 37 | */ 38 | public class TaskAdapter extends RecyclerView.Adapter { 39 | 40 | // Constant for date format 41 | private static final String DATE_FORMAT = "dd/MM/yyy"; 42 | 43 | // Member variable to handle item clicks 44 | final private ItemClickListener mItemClickListener; 45 | // Class variables for the List that holds task data and the Context 46 | private List mTaskEntries; 47 | private Context mContext; 48 | // Date formatter 49 | private SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault()); 50 | 51 | /** 52 | * Constructor for the TaskAdapter that initializes the Context. 53 | * 54 | * @param context the current Context 55 | * @param listener the ItemClickListener 56 | */ 57 | public TaskAdapter(Context context, ItemClickListener listener) { 58 | mContext = context; 59 | mItemClickListener = listener; 60 | } 61 | 62 | /** 63 | * Called when ViewHolders are created to fill a RecyclerView. 64 | * 65 | * @return A new TaskViewHolder that holds the view for each task 66 | */ 67 | @Override 68 | public TaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 69 | // Inflate the task_layout to a view 70 | View view = LayoutInflater.from(mContext) 71 | .inflate(R.layout.task_layout, parent, false); 72 | 73 | return new TaskViewHolder(view); 74 | } 75 | 76 | /** 77 | * Called by the RecyclerView to display data at a specified position in the Cursor. 78 | * 79 | * @param holder The ViewHolder to bind Cursor data to 80 | * @param position The position of the data in the Cursor 81 | */ 82 | @Override 83 | public void onBindViewHolder(TaskViewHolder holder, int position) { 84 | // Determine the values of the wanted data 85 | TaskEntry taskEntry = mTaskEntries.get(position); 86 | String description = taskEntry.getDescription(); 87 | int priority = taskEntry.getPriority(); 88 | String updatedAt = dateFormat.format(taskEntry.getUpdatedAt()); 89 | 90 | //Set values 91 | holder.taskDescriptionView.setText(description); 92 | holder.updatedAtView.setText(updatedAt); 93 | 94 | // Programmatically set the text and color for the priority TextView 95 | String priorityString = "" + priority; // converts int to String 96 | holder.priorityView.setText(priorityString); 97 | 98 | GradientDrawable priorityCircle = (GradientDrawable) holder.priorityView.getBackground(); 99 | // Get the appropriate background color based on the priority 100 | int priorityColor = getPriorityColor(priority); 101 | priorityCircle.setColor(priorityColor); 102 | } 103 | 104 | /* 105 | Helper method for selecting the correct priority circle color. 106 | P1 = red, P2 = orange, P3 = yellow 107 | */ 108 | private int getPriorityColor(int priority) { 109 | int priorityColor = 0; 110 | 111 | switch (priority) { 112 | case 1: 113 | priorityColor = ContextCompat.getColor(mContext, R.color.materialRed); 114 | break; 115 | case 2: 116 | priorityColor = ContextCompat.getColor(mContext, R.color.materialOrange); 117 | break; 118 | case 3: 119 | priorityColor = ContextCompat.getColor(mContext, R.color.materialYellow); 120 | break; 121 | default: 122 | break; 123 | } 124 | return priorityColor; 125 | } 126 | 127 | /** 128 | * Returns the number of items to display. 129 | */ 130 | @Override 131 | public int getItemCount() { 132 | if (mTaskEntries == null) { 133 | return 0; 134 | } 135 | return mTaskEntries.size(); 136 | } 137 | 138 | public List getTasks() { 139 | return mTaskEntries; 140 | } 141 | 142 | /** 143 | * When data changes, this method updates the list of taskEntries 144 | * and notifies the adapter to use the new values on it 145 | */ 146 | public void setTasks(List taskEntries) { 147 | mTaskEntries = taskEntries; 148 | notifyDataSetChanged(); 149 | } 150 | 151 | public interface ItemClickListener { 152 | void onItemClickListener(int itemId); 153 | } 154 | 155 | // Inner class for creating ViewHolders 156 | class TaskViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 157 | 158 | // Class variables for the task description and priority TextViews 159 | TextView taskDescriptionView; 160 | TextView updatedAtView; 161 | TextView priorityView; 162 | 163 | /** 164 | * Constructor for the TaskViewHolders. 165 | * 166 | * @param itemView The view inflated in onCreateViewHolder 167 | */ 168 | public TaskViewHolder(View itemView) { 169 | super(itemView); 170 | 171 | taskDescriptionView = itemView.findViewById(R.id.taskDescription); 172 | updatedAtView = itemView.findViewById(R.id.taskUpdatedAt); 173 | priorityView = itemView.findViewById(R.id.priorityTextView); 174 | itemView.setOnClickListener(this); 175 | } 176 | 177 | @Override 178 | public void onClick(View view) { 179 | int elementId = mTaskEntries.get(getAdapterPosition()).getId(); 180 | mItemClickListener.onItemClickListener(elementId); 181 | } 182 | } 183 | } -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/database/AppDatabase.java: -------------------------------------------------------------------------------- 1 | package com.example.android.todolist.database; 2 | 3 | import android.arch.persistence.room.Database; 4 | import android.arch.persistence.room.Room; 5 | import android.arch.persistence.room.RoomDatabase; 6 | import android.arch.persistence.room.TypeConverters; 7 | import android.content.Context; 8 | import android.util.Log; 9 | 10 | @Database(entities = {TaskEntry.class}, version = 1, exportSchema = false) 11 | @TypeConverters(DateConverter.class) 12 | public abstract class AppDatabase extends RoomDatabase { 13 | 14 | private static final String LOG_TAG = AppDatabase.class.getSimpleName(); 15 | private static final Object LOCK = new Object(); 16 | private static final String DATABASE_NAME = "todolist"; 17 | private static AppDatabase sInstance; 18 | 19 | public static AppDatabase getInstance(Context context) { 20 | if (sInstance == null) { 21 | synchronized (LOCK) { 22 | Log.d(LOG_TAG, "Creating new database instance"); 23 | sInstance = Room.databaseBuilder(context.getApplicationContext(), 24 | AppDatabase.class, AppDatabase.DATABASE_NAME) 25 | .build(); 26 | } 27 | } 28 | Log.d(LOG_TAG, "Getting the database instance"); 29 | return sInstance; 30 | } 31 | 32 | public abstract TaskDao taskDao(); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/database/DateConverter.java: -------------------------------------------------------------------------------- 1 | package com.example.android.todolist.database; 2 | 3 | import android.arch.persistence.room.TypeConverter; 4 | 5 | import java.util.Date; 6 | 7 | public class DateConverter { 8 | @TypeConverter 9 | public static Date toDate(Long timestamp) { 10 | return timestamp == null ? null : new Date(timestamp); 11 | } 12 | 13 | @TypeConverter 14 | public static Long toTimestamp(Date date) { 15 | return date == null ? null : date.getTime(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/database/TaskDao.java: -------------------------------------------------------------------------------- 1 | package com.example.android.todolist.database; 2 | 3 | import android.arch.lifecycle.LiveData; 4 | import android.arch.persistence.room.Dao; 5 | import android.arch.persistence.room.Delete; 6 | import android.arch.persistence.room.Insert; 7 | import android.arch.persistence.room.OnConflictStrategy; 8 | import android.arch.persistence.room.Query; 9 | import android.arch.persistence.room.Update; 10 | 11 | import java.util.List; 12 | 13 | @Dao 14 | public interface TaskDao { 15 | 16 | @Query("SELECT * FROM task ORDER BY priority") 17 | LiveData> loadAllTasks(); 18 | 19 | @Insert 20 | void insertTask(TaskEntry taskEntry); 21 | 22 | @Update(onConflict = OnConflictStrategy.REPLACE) 23 | void updateTask(TaskEntry taskEntry); 24 | 25 | @Delete 26 | void deleteTask(TaskEntry taskEntry); 27 | 28 | @Query("SELECT * FROM task WHERE id = :id") 29 | LiveData loadTaskById(int id); 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/android/todolist/database/TaskEntry.java: -------------------------------------------------------------------------------- 1 | package com.example.android.todolist.database; 2 | 3 | import android.arch.persistence.room.ColumnInfo; 4 | import android.arch.persistence.room.Entity; 5 | import android.arch.persistence.room.Ignore; 6 | import android.arch.persistence.room.PrimaryKey; 7 | 8 | import java.util.Date; 9 | 10 | @Entity(tableName = "task") 11 | public class TaskEntry { 12 | 13 | @PrimaryKey(autoGenerate = true) 14 | private int id; 15 | private String description; 16 | private int priority; 17 | @ColumnInfo(name = "updated_at") 18 | private Date updatedAt; 19 | 20 | @Ignore 21 | public TaskEntry(String description, int priority, Date updatedAt) { 22 | this.description = description; 23 | this.priority = priority; 24 | this.updatedAt = updatedAt; 25 | } 26 | 27 | public TaskEntry(int id, String description, int priority, Date updatedAt) { 28 | this.id = id; 29 | this.description = description; 30 | this.priority = priority; 31 | this.updatedAt = updatedAt; 32 | } 33 | 34 | public int getId() { 35 | return id; 36 | } 37 | 38 | public void setId(int id) { 39 | this.id = id; 40 | } 41 | 42 | public String getDescription() { 43 | return description; 44 | } 45 | 46 | public void setDescription(String description) { 47 | this.description = description; 48 | } 49 | 50 | public int getPriority() { 51 | return priority; 52 | } 53 | 54 | public void setPriority(int priority) { 55 | this.priority = priority; 56 | } 57 | 58 | public Date getUpdatedAt() { 59 | return updatedAt; 60 | } 61 | 62 | public void setUpdatedAt(Date updatedAt) { 63 | this.updatedAt = updatedAt; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/priority_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_add_task.xml: -------------------------------------------------------------------------------- 1 | 5 | 15 | 16 | 17 | 24 | 25 | 26 | 35 | 36 | 37 | 45 | 46 | 55 | 56 | 64 | 65 | 73 | 74 | 75 | 76 | 77 |