├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── 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 │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── drawable │ │ │ │ └── priority_circle.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ └── layout │ │ │ │ ├── activity_main.xml │ │ │ │ ├── task_layout.xml │ │ │ │ └── activity_add_task.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── udacity │ │ │ └── todolist │ │ │ ├── data │ │ │ ├── TaskDbHelper.java │ │ │ ├── TaskContract.java │ │ │ └── TaskContentProvider.java │ │ │ ├── AddTaskActivity.java │ │ │ ├── CustomCursorAdapter.java │ │ │ └── MainActivity.java │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── udacity │ │ │ └── todolist │ │ │ └── ExampleUnitTest.java │ └── androidTest │ │ └── java │ │ └── com │ │ └── example │ │ └── udacity │ │ └── todolist │ │ ├── utils │ │ └── PollingCheck.java │ │ └── data │ │ ├── TestUtilities.java │ │ └── TestTaskContentProvider.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── README.md ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ToDoList/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ToDoList/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ToDoList/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ToDoList/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ToDoList/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/ToDoList/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Aug 22 13:04:16 PDT 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/priority_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/test/java/com/example/udacity/todolist/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.udacity.todolist; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #3F51B5 5 | #303F9F 6 | #C5CAE9 7 | #303F9F 8 | 9 | 10 | #E74C3C 11 | #E67E22 12 | #F1C40F 13 | 14 | 15 | -------------------------------------------------------------------------------- /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/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/cezannec/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | To-Do List 3 | All Tasks 4 | Add a New Task 5 | 6 | 7 | 1 8 | 2 9 | 3 10 | 11 | 12 | 13 | Describe your task… 14 | Priority 15 | 16 | High 17 | Medium 18 | Low 19 | 20 | Add 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 10 | 11 | 12 | 17 | 18 | 19 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "24.0.3" 6 | defaultConfig { 7 | applicationId "com.example.udacity.todolist" 8 | minSdkVersion 15 9 | targetSdkVersion 24 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:24.2.1' 25 | compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha8' 26 | 27 | //add Recycler view dependencies; must match SDK version (24) 28 | compile 'com.android.support:recyclerview-v7:24.2.1' 29 | 30 | //FAB dependencies 31 | compile 'com.android.support:design:24.2.1' 32 | 33 | //Testing 34 | // Instrumentation dependencies use androidTestCompile 35 | // (as opposed to testCompile for local unit tests run in the JVM) 36 | androidTestCompile 'junit:junit:4.12' 37 | androidTestCompile 'com.android.support:support-annotations:24.2.1' 38 | androidTestCompile 'com.android.support.test:runner:0.5' 39 | androidTestCompile 'com.android.support.test:rules:0.5' 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/udacity/todolist/utils/PollingCheck.java: -------------------------------------------------------------------------------- 1 | package com.example.udacity.todolist.utils; 2 | 3 | import junit.framework.Assert; 4 | 5 | import java.util.concurrent.Callable; 6 | 7 | 8 | public abstract class PollingCheck { 9 | 10 | private static final long TIME_SLICE = 50; 11 | private long mTimeout = 3000; 12 | 13 | public PollingCheck() { 14 | } 15 | 16 | public PollingCheck(long timeout) { 17 | mTimeout = timeout; 18 | } 19 | 20 | public static void check(CharSequence message, long timeout, Callable condition) 21 | throws Exception { 22 | while (timeout > 0) { 23 | if (condition.call()) { 24 | return; 25 | } 26 | 27 | Thread.sleep(TIME_SLICE); 28 | timeout -= TIME_SLICE; 29 | } 30 | 31 | Assert.fail(message.toString()); 32 | } 33 | 34 | protected abstract boolean check(); 35 | 36 | public void run() { 37 | if (check()) { 38 | return; 39 | } 40 | 41 | long timeout = mTimeout; 42 | while (timeout > 0) { 43 | try { 44 | Thread.sleep(TIME_SLICE); 45 | } catch (InterruptedException e) { 46 | Assert.fail("Notification error, unexpected InterruptedException"); 47 | } 48 | 49 | if (check()) { 50 | return; 51 | } 52 | 53 | timeout -= TIME_SLICE; 54 | } 55 | 56 | Assert.fail("Notification not set, unexpected timeout"); 57 | } 58 | } -------------------------------------------------------------------------------- /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 | 31 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/udacity/todolist/data/TaskDbHelper.java: -------------------------------------------------------------------------------- 1 | package com.example.udacity.todolist.data; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | import com.example.udacity.todolist.data.TaskContract.TaskEntry; 8 | 9 | 10 | public class TaskDbHelper extends SQLiteOpenHelper { 11 | 12 | // The name of the database 13 | private static final String DATABASE_NAME = "TasksDb.db"; 14 | 15 | // If you change the database schema, you must increment the database version 16 | private static final int VERSION = 10; 17 | 18 | 19 | // Constructor 20 | TaskDbHelper(Context context) { 21 | super(context, DATABASE_NAME, null, VERSION); 22 | } 23 | 24 | 25 | /** 26 | * Called when the tasks database is created for the first time. 27 | */ 28 | @Override 29 | public void onCreate(SQLiteDatabase db) { 30 | 31 | // Create tasks table (careful to follow SQL formatting rules) 32 | final String CREATE_TABLE = "CREATE TABLE " + TaskEntry.TABLE_NAME + " (" + 33 | TaskEntry._ID + " INTEGER PRIMARY KEY, " + 34 | TaskEntry.COLUMN_DESCRIPTION + " TEXT NOT NULL, " + 35 | TaskEntry.COLUMN_PRIORITY + " INTEGER NOT NULL);"; 36 | 37 | db.execSQL(CREATE_TABLE); 38 | } 39 | 40 | 41 | /** 42 | * This method discards the old table of data and calls onCreate to recreate a new one. 43 | * This only occurs when the version number for this database (DATABASE_VERSION) is incremented. 44 | */ 45 | @Override 46 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 47 | db.execSQL("DROP TABLE IF EXISTS " + TaskEntry.TABLE_NAME); 48 | onCreate(db); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/res/layout/task_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 11 | 12 | 16 | 23 | 24 | 25 | 31 | 32 | 33 | 37 | 38 | 39 | 49 | 50 | 51 | 52 | 53 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/udacity/todolist/AddTaskActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.udacity.todolist; 2 | 3 | import android.content.ContentValues; 4 | import android.net.Uri; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.EditText; 9 | import android.widget.RadioButton; 10 | import android.widget.Toast; 11 | 12 | import com.example.udacity.todolist.data.TaskContract; 13 | 14 | 15 | public class AddTaskActivity extends AppCompatActivity { 16 | 17 | // Declare a member variable to keep track of a task's selected mPriority 18 | private int mPriority; 19 | 20 | 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_add_task); 24 | 25 | // Initialize to highest mPriority by default (mPriority = 1) 26 | ((RadioButton) findViewById(R.id.radButton1)).setChecked(true); 27 | mPriority = 1; 28 | } 29 | 30 | 31 | /** 32 | * onClickAddTask is called when the "ADD" button is clicked. 33 | * It retrieves user input and inserts that new task data into the underlying database. 34 | */ 35 | public void onClickAddTask(View view) { 36 | // Check if the EditText input is empty - (don't create an entry if there is no input) 37 | String input = ((EditText) findViewById(R.id.editTextTaskDescription)).getText().toString(); 38 | if (input.length() == 0) { 39 | return; 40 | } 41 | 42 | // TODO: 5.6. Retrieve user input and store it in a ContentValues object 43 | // [Hint] Don't forget to call finish() to return to MainActivity after this insert is complete 44 | 45 | // Create new empty ContentValues object 46 | ContentValues contentValues = new ContentValues(); 47 | 48 | // Put the task description and selected mPriority into the ContentValues 49 | contentValues.put(TaskContract.TaskEntry.COLUMN_DESCRIPTION, input); 50 | contentValues.put(TaskContract.TaskEntry.COLUMN_PRIORITY, mPriority); 51 | 52 | // TODO: 5.7. Insert new task data via a ContentResolver 53 | Uri uri = getContentResolver().insert(TaskContract.TaskEntry.CONTENT_URI, contentValues); 54 | 55 | // TODO: 5.8. Display the URI that's returned with a Toast 56 | if(uri != null) { 57 | Toast.makeText(getBaseContext(), uri.toString(), Toast.LENGTH_LONG).show(); 58 | } 59 | 60 | // Finish activity (this returns back to MainActivity) 61 | finish(); 62 | } 63 | 64 | 65 | /** 66 | * onPrioritySelected is called whenever a priority button is clicked. 67 | * It changes the value of mPriority based on the selected button. 68 | */ 69 | public void onPrioritySelected(View view) { 70 | if (((RadioButton) findViewById(R.id.radButton1)).isChecked()) { 71 | mPriority = 1; 72 | } else if (((RadioButton) findViewById(R.id.radButton2)).isChecked()) { 73 | mPriority = 2; 74 | } else if (((RadioButton) findViewById(R.id.radButton3)).isChecked()) { 75 | mPriority = 3; 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/example/udacity/todolist/data/TestUtilities.java: -------------------------------------------------------------------------------- 1 | package com.example.udacity.todolist.data; 2 | 3 | import android.database.ContentObserver; 4 | import android.net.Uri; 5 | import android.os.Handler; 6 | import android.os.HandlerThread; 7 | 8 | import com.example.udacity.todolist.utils.PollingCheck; 9 | 10 | 11 | class TestUtilities { 12 | 13 | static TestContentObserver getTestContentObserver() { 14 | return TestContentObserver.getTestContentObserver(); 15 | } 16 | 17 | /** 18 | * Students: The test functions for insert and delete use TestContentObserver to test 19 | * the ContentObserver callbacks using the PollingCheck class from the Android Compatibility 20 | * Test Suite tests. 21 | * NOTE: This only tests that the onChange function is called; it DOES NOT test that the 22 | * correct Uri is returned. 23 | */ 24 | static class TestContentObserver extends ContentObserver { 25 | final HandlerThread mHT; 26 | boolean mContentChanged; 27 | 28 | private TestContentObserver(HandlerThread ht) { 29 | super(new Handler(ht.getLooper())); 30 | mHT = ht; 31 | } 32 | 33 | static TestContentObserver getTestContentObserver() { 34 | HandlerThread ht = new HandlerThread("ContentObserverThread"); 35 | ht.start(); 36 | return new TestContentObserver(ht); 37 | } 38 | 39 | /** 40 | * Called when a content change occurs. 41 | *

42 | * To ensure correct operation on older versions of the framework that did not provide a 43 | * Uri argument, applications should also implement this method whenever they implement 44 | * the { #onChange(boolean, Uri)} overload. 45 | * 46 | * @param selfChange True if this is a self-change notification. 47 | */ 48 | @Override 49 | public void onChange(boolean selfChange) { 50 | onChange(selfChange, null); 51 | } 52 | 53 | /** 54 | * Called when a content change occurs. Includes the changed content Uri when available. 55 | * 56 | * @param selfChange True if this is a self-change notification. 57 | * @param uri The Uri of the changed content, or null if unknown. 58 | */ 59 | @Override 60 | public void onChange(boolean selfChange, Uri uri) { 61 | mContentChanged = true; 62 | } 63 | 64 | /** 65 | * Note: The PollingCheck class is taken from the Android CTS (Compatibility Test Suite). 66 | * It's useful to look at the Android CTS source for ideas on how to test your Android 67 | * applications. The reason that PollingCheck works is that, by default, the JUnit testing 68 | * framework is not running on the main Android application thread. 69 | */ 70 | void waitForNotificationOrFail() { 71 | 72 | new PollingCheck(5000) { 73 | @Override 74 | protected boolean check() { 75 | return mContentChanged; 76 | } 77 | }.run(); 78 | mHT.quit(); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/udacity/todolist/data/TaskContract.java: -------------------------------------------------------------------------------- 1 | package com.example.udacity.todolist.data; 2 | 3 | import android.content.ContentResolver; 4 | import android.net.Uri; 5 | import android.provider.BaseColumns; 6 | 7 | 8 | public class TaskContract { 9 | 10 | /* TODO: 3.1. Add content provider constants to the Contract 11 | Clients need to know how to access the task data, and it's your job to provide 12 | these content URI's for the path to that data: 13 | 1) Content authority, 14 | 2) Base content URI, 15 | 3) Path(s) to the tasks directory 16 | 4) Content URI for data in the TaskEntry class 17 | */ 18 | 19 | // The authority, which is how your code knows which Content Provider to access 20 | public static final String AUTHORITY = "com.example.udacity.todolist"; 21 | 22 | // The base content URI = "content://" + 23 | public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + AUTHORITY); 24 | 25 | // Define the possible paths for accessing data in this contract 26 | // This is the path for the "tasks" directory 27 | public static final String PATH_TASKS = "tasks"; 28 | 29 | 30 | /* TaskEntry is an inner class that defines the contents of the task table */ 31 | public static final class TaskEntry implements BaseColumns { 32 | 33 | // TaskEntry content URI = base content URI + path 34 | public static final Uri CONTENT_URI = 35 | BASE_CONTENT_URI.buildUpon().appendPath(PATH_TASKS).build(); 36 | 37 | // Task table and column names 38 | public static final String TABLE_NAME = "tasks"; 39 | 40 | // Since TaskEntry implements the interface "BaseColumns", it has an automatically produced 41 | // "_ID" column in addition to the two below 42 | public static final String COLUMN_DESCRIPTION = "description"; 43 | public static final String COLUMN_PRIORITY = "priority"; 44 | 45 | 46 | /* 47 | The above table structure looks something like the sample table below. 48 | With the name of the table and columns on top, and potential contents in rows 49 | 50 | Note: Because this implements BaseColumns, the _id column is generated automatically 51 | 52 | tasks 53 | - - - - - - - - - - - - - - - - - - - - - - 54 | | _id | description | priority | 55 | - - - - - - - - - - - - - - - - - - - - - - 56 | | 1 | Complete lesson | 1 | 57 | - - - - - - - - - - - - - - - - - - - - - - 58 | | 2 | Go shopping | 3 | 59 | - - - - - - - - - - - - - - - - - - - - - - 60 | . 61 | . 62 | . 63 | - - - - - - - - - - - - - - - - - - - - - - 64 | | 43 | Learn guitar | 2 | 65 | - - - - - - - - - - - - - - - - - - - - - - 66 | 67 | */ 68 | 69 | 70 | /* 71 | (Included) MIME types used in TaskContentProvider's getType() method . 72 | There are generally two MIME types of data we will be working with: 73 | 1) a directory of items, which can be a row or set of rows 74 | OR 2) a single item, which is a single row of data 75 | */ 76 | 77 | static final String CONTENT_TYPE = 78 | ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + AUTHORITY + "/" + PATH_TASKS; 79 | 80 | static final String CONTENT_ITEM_TYPE = 81 | ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + AUTHORITY + "/" + PATH_TASKS; 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn ( ) { 37 | echo "$*" 38 | } 39 | 40 | die ( ) { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 165 | if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then 166 | cd "$(dirname "$0")" 167 | fi 168 | 169 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 170 | -------------------------------------------------------------------------------- /app/src/main/java/com/example/udacity/todolist/CustomCursorAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.udacity.todolist; 2 | 3 | import android.content.Context; 4 | import android.database.Cursor; 5 | import android.graphics.drawable.GradientDrawable; 6 | import android.support.v4.content.ContextCompat; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.TextView; 12 | 13 | import com.example.udacity.todolist.data.TaskContract; 14 | 15 | 16 | /** 17 | * This CustomCursorAdapter creates and binds ViewHolders, that hold the description and priority of a task, 18 | * to a RecyclerView to efficiently display data. 19 | */ 20 | public class CustomCursorAdapter extends RecyclerView.Adapter { 21 | 22 | // Class variables for the Cursor that holds task data and the Context 23 | private Cursor mCursor; 24 | private Context mContext; 25 | 26 | 27 | /** 28 | * Constructor for the CustomCursorAdapter that initializes the Context. 29 | * 30 | * @param mContext the current Context 31 | */ 32 | public CustomCursorAdapter(Context mContext) { 33 | this.mContext = mContext; 34 | } 35 | 36 | 37 | /** 38 | * Called when ViewHolders are created to fill a RecyclerView. 39 | * 40 | * @return A new TaskViewHolder that holds the view for each task 41 | */ 42 | @Override 43 | public TaskViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 44 | 45 | // Inflate the task_layout to a view 46 | View view = LayoutInflater.from(mContext) 47 | .inflate(R.layout.task_layout, parent, false); 48 | 49 | return new TaskViewHolder(view); 50 | } 51 | 52 | 53 | /** 54 | * Called by the RecyclerView to display data at a specified position in the Cursor. 55 | * 56 | * @param holder The ViewHolder to bind Cursor data to 57 | * @param position The position of the data in the Cursor 58 | */ 59 | @Override 60 | public void onBindViewHolder(TaskViewHolder holder, int position) { 61 | 62 | // Indices for the _id, description, and priority columns 63 | int idIndex = mCursor.getColumnIndex(TaskContract.TaskEntry._ID); 64 | int descriptionIndex = mCursor.getColumnIndex(TaskContract.TaskEntry.COLUMN_DESCRIPTION); 65 | int priorityIndex = mCursor.getColumnIndex(TaskContract.TaskEntry.COLUMN_PRIORITY); 66 | 67 | mCursor.moveToPosition(position); // get to the right location in the cursor 68 | 69 | // Determine the values of the wanted data 70 | final int id = mCursor.getInt(idIndex); 71 | String description = mCursor.getString(descriptionIndex); 72 | int priority = mCursor.getInt(priorityIndex); 73 | 74 | //Set values 75 | holder.itemView.setTag(id); 76 | holder.taskDescriptionView.setText(description); 77 | 78 | // Programmatically set the text and color for the priority TextView 79 | String priorityString = "" + priority; // converts int to String 80 | holder.priorityView.setText(priorityString); 81 | 82 | GradientDrawable priorityCircle = (GradientDrawable) holder.priorityView.getBackground(); 83 | // Get the appropriate background color based on the priority 84 | int priorityColor = getPriorityColor(priority); 85 | priorityCircle.setColor(priorityColor); 86 | 87 | } 88 | 89 | 90 | /* 91 | Helper method for selecting the correct priority circle color. 92 | P1 = red, P2 = orange, P3 = yellow 93 | */ 94 | private int getPriorityColor(int priority) { 95 | int priorityColor = 0; 96 | 97 | switch(priority) { 98 | case 1: priorityColor = ContextCompat.getColor(mContext, R.color.materialRed); 99 | break; 100 | case 2: priorityColor = ContextCompat.getColor(mContext, R.color.materialOrange); 101 | break; 102 | case 3: priorityColor = ContextCompat.getColor(mContext, R.color.materialYellow); 103 | break; 104 | default: break; 105 | } 106 | return priorityColor; 107 | } 108 | 109 | 110 | /** 111 | * Returns the number of items to display. 112 | */ 113 | @Override 114 | public int getItemCount() { 115 | if (mCursor == null) { 116 | return 0; 117 | } 118 | return mCursor.getCount(); 119 | } 120 | 121 | 122 | /** 123 | * When data changes and a re-query occurs, this function swaps the old Cursor 124 | * with a newly updated Cursor (Cursor c) that is passed in. 125 | */ 126 | public Cursor swapCursor(Cursor c) { 127 | // check if this cursor is the same as the previous cursor (mCursor) 128 | if (mCursor == c) { 129 | return null; // bc nothing has changed 130 | } 131 | Cursor temp = mCursor; 132 | this.mCursor = c; // new cursor value assigned 133 | 134 | //check if this is a valid cursor, then update the cursor 135 | if (c != null) { 136 | this.notifyDataSetChanged(); 137 | } 138 | return temp; 139 | } 140 | 141 | 142 | // Inner class for creating ViewHolders 143 | class TaskViewHolder extends RecyclerView.ViewHolder { 144 | 145 | // Class variables for the task description and priority TextViews 146 | TextView taskDescriptionView; 147 | TextView priorityView; 148 | 149 | /** 150 | * Constructor for the TaskViewHolders. 151 | * 152 | * @param itemView The view inflated in onCreateViewHolder 153 | */ 154 | public TaskViewHolder(View itemView) { 155 | super(itemView); 156 | 157 | taskDescriptionView = (TextView) itemView.findViewById(R.id.taskDescription); 158 | priorityView = (TextView) itemView.findViewById(R.id.priorityTextView); 159 | } 160 | } 161 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_add_task.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 16 | 17 | 18 | 25 | 26 | 27 | 36 | 37 | 41 | 42 | 44 | 50 | 51 |