diff --git a/README.md b/README.md index 3bc526f86..1ad852e74 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -# Android Architecture Blueprints [beta] - MVP + Dagger2 +# Android Architecture Blueprints - MVP + Dagger2 ### Summary -Project contributors: [Saúl Molinero](https://github.com/saulmm) - ### Key concepts [Dagger2](http://google.github.io/dagger/) is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier version created by Square and now maintained by Google. @@ -55,11 +53,12 @@ Very high. Use of Dagger2 improves flexibility in local integration tests and UI ------------------------------------------------------------------------------- Language files blank comment code ------------------------------------------------------------------------------- -Java 60 1175 1610 3699 (3451 in MVP) -XML 34 97 337 602 +Java 58 1193 1658 3901 (3451 in MVP) +XML 34 97 338 610 ------------------------------------------------------------------------------- -SUM: 94 1272 1947 4301 +SUM: 92 1290 1996 4511 ------------------------------------------------------------------------------- + ``` ### Maintainability diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..04e89c794 --- /dev/null +++ b/circle.yml @@ -0,0 +1,26 @@ +machine: + java: + version: oraclejdk8 + environment: + GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"' + +dependencies: + pre: + - sudo pip install -U crcmod + - echo y | android update sdk --no-ui --all --filter "tools,platform-tools,build-tools-26.0.1,android-26,extra-android-m2repository" + + post: + - cd todoapp;./gradlew :app:assembleDebug -PdisablePreDex + - cd todoapp;./gradlew :app:assembleAndroidTest -PdisablePreDex + - echo ${GCLOUD_SERVICE_KEY} | base64 --decode > ${HOME}/client-secret.json + - sudo /opt/google-cloud-sdk/bin/gcloud config set project android-devrel-ci + - sudo /opt/google-cloud-sdk/bin/gcloud --quiet components update + - sudo /opt/google-cloud-sdk/bin/gcloud auth activate-service-account travis-ci-for-blueprints@android-devrel-ci.iam.gserviceaccount.com --key-file ${HOME}/client-secret.json + +test: + override: + - echo "y" | sudo /opt/google-cloud-sdk/bin/gcloud firebase test android run --app todoapp/app/build/outputs/apk/app-mock-debug.apk --test todoapp/app/build/outputs/apk/app-mock-debug-androidTest.apk -d Nexus5X,Nexus10 -v 21,25 -l fr --results-bucket cloud-test-android-devrel-ci + post: + - sudo /opt/google-cloud-sdk/bin/gsutil -m cp -r -U `sudo /opt/google-cloud-sdk/bin/gsutil ls gs://cloud-test-android-devrel-ci | tail -1` $CIRCLE_ARTIFACTS/ | true + - mkdir -p $CIRCLE_TEST_REPORTS/junit/ + - find $CIRCLE_ARTIFACTS -name \*.xml -exec cp {} $CIRCLE_TEST_REPORTS/junit/ \; diff --git a/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/data/TasksLocalDataSourceTest.java b/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksLocalDataSourceTest.java similarity index 99% rename from todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/data/TasksLocalDataSourceTest.java rename to todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksLocalDataSourceTest.java index 7408434b3..754e189b9 100644 --- a/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/data/TasksLocalDataSourceTest.java +++ b/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksLocalDataSourceTest.java @@ -212,7 +212,7 @@ public void onTasksLoaded(List tasks) { boolean newTask1IdFound = false; boolean newTask2IdFound = false; - for (Task task: tasks) { + for (Task task : tasks) { if (task.getId().equals(newTask1.getId())) { newTask1IdFound = true; } diff --git a/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.java b/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.java index 4516948fd..9dd59db3f 100644 --- a/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.java +++ b/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/AppNavigationTest.java @@ -17,6 +17,7 @@ package com.example.android.architecture.blueprints.todoapp.tasks; import android.support.test.espresso.IdlingRegistry; +import android.support.test.espresso.NoActivityResumedException; import android.support.test.filters.LargeTest; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; @@ -33,6 +34,7 @@ import org.junit.runner.RunWith; import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.Espresso.pressBack; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.assertion.ViewAssertions.matches; import static android.support.test.espresso.contrib.DrawerActions.open; @@ -43,6 +45,7 @@ import static android.support.test.espresso.matcher.ViewMatchers.withId; import static com.example.android.architecture.blueprints.todoapp.TestUtils.getToolbarNavigationContentDescription; import static com.example.android.architecture.blueprints.todoapp.custom.action.NavigationViewActions.navigateTo; +import static junit.framework.Assert.fail; /** * Tests for the {@link DrawerLayout} layout component in {@link TasksActivity} which manages @@ -83,14 +86,7 @@ public void unregisterIdlingResource() { @Test public void clickOnStatisticsNavigationItem_ShowsStatisticsScreen() { - // Open Drawer to click on navigation. - onView(withId(R.id.drawer_layout)) - .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed. - .perform(open()); // Open Drawer - - // Start statistics screen. - onView(withId(R.id.nav_view)) - .perform(navigateTo(R.id.statistics_navigation_menu_item)); + openStatisticsScreen(); // Check that statistics Activity was opened. onView(withId(R.id.statistics)).check(matches(isDisplayed())); @@ -98,23 +94,9 @@ public void clickOnStatisticsNavigationItem_ShowsStatisticsScreen() { @Test public void clickOnListNavigationItem_ShowsListScreen() { - // Open Drawer to click on navigation. - onView(withId(R.id.drawer_layout)) - .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed. - .perform(open()); // Open Drawer - - // Start statistics screen. - onView(withId(R.id.nav_view)) - .perform(navigateTo(R.id.statistics_navigation_menu_item)); + openStatisticsScreen(); - // Open Drawer to click on navigation. - onView(withId(R.id.drawer_layout)) - .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed. - .perform(open()); // Open Drawer - - // Start tasks list screen. - onView(withId(R.id.nav_view)) - .perform(navigateTo(R.id.list_navigation_menu_item)); + openTasksScreen(); // Check that Tasks Activity was opened. onView(withId(R.id.tasksContainer)).check(matches(isDisplayed())); @@ -134,4 +116,66 @@ public void clickOnAndroidHomeIcon_OpensNavigation() { onView(withId(R.id.drawer_layout)) .check(matches(isOpen(Gravity.LEFT))); // Left drawer is open open. } + + @Test + public void Statistics_backNavigatesToTasks() { + openStatisticsScreen(); + + // Press back to go back to the tasks list + pressBack(); + + // Check that Tasks Activity was restored. + onView(withId(R.id.tasksContainer)).check(matches(isDisplayed())); + } + + @Test + public void backFromTasksScreen_ExitsApp() { + // From the tasks screen, press back should exit the app. + assertPressingBackExitsApp(); + } + + @Test + public void backFromTasksScreenAfterStats_ExitsApp() { + // This test checks that TasksActivity is a parent of StatisticsActivity + + // Open the stats screen + openStatisticsScreen(); + + // Open the tasks screen to restore the task + openTasksScreen(); + + // Pressing back should exit app + assertPressingBackExitsApp(); + } + + private void assertPressingBackExitsApp() { + try { + pressBack(); + fail("Should kill the app and throw an exception"); + } catch (NoActivityResumedException e) { + // Test OK + } + } + + private void openTasksScreen() { + // Open Drawer to click on navigation item. + onView(withId(R.id.drawer_layout)) + .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed. + .perform(open()); // Open Drawer + + // Start tasks list screen. + onView(withId(R.id.nav_view)) + .perform(navigateTo(R.id.list_navigation_menu_item)); + } + + private void openStatisticsScreen() { + // Open Drawer to click on navigation item. + onView(withId(R.id.drawer_layout)) + .check(matches(isClosed(Gravity.LEFT))) // Left Drawer should be closed. + .perform(open()); // Open Drawer + + // Start statistics screen. + onView(withId(R.id.nav_view)) + .perform(navigateTo(R.id.statistics_navigation_menu_item)); + } } diff --git a/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksScreenTest.java b/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksScreenTest.java index 6d635ef3f..e621af026 100644 --- a/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksScreenTest.java +++ b/todoapp/app/src/androidTest/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksScreenTest.java @@ -19,6 +19,7 @@ import android.support.test.InstrumentationRegistry; import android.support.test.espresso.IdlingRegistry; import android.support.test.filters.LargeTest; +import android.support.test.filters.SdkSuppress; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.text.TextUtils; @@ -213,7 +214,6 @@ public void markTaskAsComplete() { @Test public void markTaskAsActive() { - viewAllTasks(); // Add completed task @@ -459,9 +459,31 @@ public void orientationChange_FilterCompletedPersists() { } @Test - public void orientationChange_DuringEdit() throws IllegalStateException { - viewAllTasks(); + @SdkSuppress(minSdkVersion = 21) // Blinking cursor after rotation breaks this in API 19 + public void orientationChange_DuringEdit_ChangePersists() throws Throwable { + // Add a completed task + createTask(TITLE1, DESCRIPTION); + + // Open the task in details view + onView(withText(TITLE1)).perform(click()); + + // Click on the edit task button + onView(withId(R.id.fab_edit_task)).perform(click()); + // Change task title (but don't save) + onView(withId(R.id.add_task_title)) + .perform(replaceText(TITLE2), closeSoftKeyboard()); // Type new task title + + // Rotate the screen + TestUtils.rotateOrientation(getCurrentActivity()); + + // Verify task title is restored + onView(withId(R.id.add_task_title)).check(matches(withText(TITLE2))); + } + + @Test + @SdkSuppress(minSdkVersion = 21) // Blinking cursor after rotation breaks this in API 19 + public void orientationChange_DuringEdit_NoDuplicate() throws IllegalStateException { // Add a completed task createTask(TITLE1, DESCRIPTION); diff --git a/todoapp/app/src/main/AndroidManifest.xml b/todoapp/app/src/main/AndroidManifest.xml index 85c073731..abf13e17f 100644 --- a/todoapp/app/src/main/AndroidManifest.xml +++ b/todoapp/app/src/main/AndroidManifest.xml @@ -15,9 +15,9 @@ ~ limitations under the License. --> - + + android:name="com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity" + android:theme="@style/AppTheme.OverlapSystemBar"> - - + + + + + diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/ToDoApplication.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/ToDoApplication.java index a48deb65e..bbf3e0612 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/ToDoApplication.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/ToDoApplication.java @@ -4,6 +4,7 @@ import android.support.annotation.VisibleForTesting; import com.example.android.architecture.blueprints.todoapp.data.source.TasksRepository; +import com.example.android.architecture.blueprints.todoapp.di.AppComponent; import com.example.android.architecture.blueprints.todoapp.di.DaggerAppComponent; import javax.inject.Inject; @@ -13,7 +14,7 @@ /** * We create a custom {@link Application} class that extends {@link DaggerApplication}. - * We then override applicationInjector() which tells Dagger how to make our @Singleton Component + * We then override applicationInjector() which tells Dagger how to make our @Singleton Component * We never have to call `component.inject(this)` as {@link DaggerApplication} will do that for us. */ public class ToDoApplication extends DaggerApplication { @@ -22,14 +23,15 @@ public class ToDoApplication extends DaggerApplication { @Override protected AndroidInjector applicationInjector() { - return DaggerAppComponent.builder().application(this).build(); + AppComponent appComponent = DaggerAppComponent.builder().application(this).build(); + appComponent.inject(this); + return appComponent; } /** * Our Espresso tests need to be able to get an instance of the {@link TasksRepository} * so that we can delete all tasks before running each test */ - @VisibleForTesting public TasksRepository getTasksRepository() { return tasksRepository; diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskActivity.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskActivity.java index 9fab66847..fd706bfb3 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskActivity.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskActivity.java @@ -36,16 +36,22 @@ public class AddEditTaskActivity extends DaggerAppCompatActivity { public static final int REQUEST_ADD_TASK = 1; + public static final String SHOULD_LOAD_DATA_FROM_REPO_KEY = "SHOULD_LOAD_DATA_FROM_REPO_KEY"; + @Inject AddEditTaskContract.Presenter mAddEditTasksPresenter; @Inject AddEditTaskFragment mFragment; - + @Inject @Nullable String mTaskId; + // In a rotation it's important to know if we want to let the framework restore view state or + // need to load data from the repository. This is saved into the state bundle. + private boolean mIsDataMissing = true; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -66,6 +72,15 @@ protected void onCreate(Bundle savedInstanceState) { ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), addEditTaskFragment, R.id.contentFrame); } + restoreState(savedInstanceState); + } + + private void restoreState(Bundle savedInstanceState) { + // Prevent the presenter from loading data from the repository if this is a config change. + if (savedInstanceState != null) { + // Data might not have loaded when the config change happen, so we saved the state. + mIsDataMissing = savedInstanceState.getBoolean(SHOULD_LOAD_DATA_FROM_REPO_KEY); + } } @NonNull @@ -78,10 +93,20 @@ private ActionBar setupToolbar() { return actionBar; } + @Override + protected void onSaveInstanceState(Bundle outState) { + // Save the state so that next time we know if we need to refresh data. + outState.putBoolean(SHOULD_LOAD_DATA_FROM_REPO_KEY, mAddEditTasksPresenter.isDataMissing()); + super.onSaveInstanceState(outState); + } @Override public boolean onSupportNavigateUp() { onBackPressed(); return true; } + + boolean isDataMissing() { + return mIsDataMissing; + } } diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskContract.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskContract.java index 92141f57e..4ef6e58fa 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskContract.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskContract.java @@ -42,5 +42,7 @@ interface Presenter extends BasePresenter { void saveTask(String title, String description); void populateTask(); + + boolean isDataMissing(); } } diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskFragment.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskFragment.java index 6edbc6dee..a04337486 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskFragment.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskFragment.java @@ -118,5 +118,4 @@ public void setDescription(String description) { public boolean isActive() { return isAdded(); } - } diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskModule.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskModule.java index 6d76eead2..6d9cebe15 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskModule.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskModule.java @@ -16,11 +16,12 @@ */ @Module public abstract class AddEditTaskModule { - //Rather than having the activity deal with getting the intent extra and passing it to the presenter - //we will provide the taskId directly into the AddEditTaskActivitySubcomponent + + // Rather than having the activity deal with getting the intent extra and passing it to the presenter + // we will provide the taskId directly into the AddEditTaskActivitySubcomponent // which is what gets generated for us by Dagger.Android. - // We can then inject our TaskId into our Presenter without having pass through dependency from the Activity. - // Each UI object gets the dependency it needs and nothing else. + // We can then inject our TaskId and state into our Presenter without having pass through dependency from + // the Activity. Each UI object gets the dependency it needs and nothing else. @Provides @ActivityScoped @Nullable @@ -28,6 +29,12 @@ static String provideTaskId(AddEditTaskActivity activity) { return activity.getIntent().getStringExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID); } + @Provides + @ActivityScoped + static boolean provideStatusDataMissing(AddEditTaskActivity activity) { + return activity.isDataMissing(); + } + @FragmentScoped @ContributesAndroidInjector abstract AddEditTaskFragment addEditTaskFragment(); diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskPresenter.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskPresenter.java index dd9db4ed9..2a86c8966 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskPresenter.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/addedittask/AddEditTaskPresenter.java @@ -25,6 +25,8 @@ import javax.inject.Inject; +import dagger.Lazy; + /** * Listens to user actions from the UI ({@link AddEditTaskFragment}), retrieves the data and * updates @@ -50,14 +52,29 @@ final class AddEditTaskPresenter implements AddEditTaskContract.Presenter, @Nullable private String mTaskId; + // This is provided lazily because its value is determined in the Activity's onCreate. By + // calling it in takeView(), the value is guaranteed to be set. + private Lazy mIsDataMissingLazy; + + // Whether the data has been loaded with this presenter (or comes from a system restore) + private boolean mIsDataMissing; + /** * Dagger strictly enforces that arguments not marked with {@code @Nullable} are not injected * with {@code @Nullable} values. + * + * @param taskId the task ID or null if it's a new task + * @param tasksRepository the data source + * @param shouldLoadDataFromRepo a flag that controls whether we should load data from the + * repository or not. It's lazy because it's determined in the + * Activity's onCreate. */ @Inject - AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksRepository tasksRepository) { + AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksRepository tasksRepository, + Lazy shouldLoadDataFromRepo) { mTaskId = taskId; mTasksRepository = tasksRepository; + mIsDataMissingLazy = shouldLoadDataFromRepo; } @Override @@ -80,7 +97,8 @@ public void populateTask() { @Override public void takeView(AddEditTaskContract.View view) { mAddTaskView = view; - if (!isNewTask()) { + mIsDataMissing = mIsDataMissingLazy.get(); + if (!isNewTask() && mIsDataMissing) { populateTask(); } } @@ -97,6 +115,7 @@ public void onTaskLoaded(Task task) { mAddTaskView.setTitle(task.getTitle()); mAddTaskView.setDescription(task.getDescription()); } + mIsDataMissing = false; } @Override @@ -107,6 +126,11 @@ public void onDataNotAvailable() { } } + @Override + public boolean isDataMissing() { + return mIsDataMissing; + } + private boolean isNewTask() { return mTaskId == null; } diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/TasksRepository.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/TasksRepository.java index 00428f5c9..6e0e99871 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/TasksRepository.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/TasksRepository.java @@ -200,7 +200,7 @@ public void clearCompletedTasks() { * Gets tasks from local data source (sqlite) unless the table is new or empty. In that case it * uses the network data source. This is done to simplify the sample. *

- * Note: {@link LoadTasksCallback#onDataNotAvailable()} is fired if both data sources fail to + * Note: {@link GetTaskCallback#onDataNotAvailable()} is fired if both data sources fail to * get the data. */ @Override @@ -222,6 +222,11 @@ public void getTask(@NonNull final String taskId, @NonNull final GetTaskCallback mTasksLocalDataSource.getTask(taskId, new GetTaskCallback() { @Override public void onTaskLoaded(Task task) { + // Do in memory cache update to keep the app UI up to date + if (mCachedTasks == null) { + mCachedTasks = new LinkedHashMap<>(); + } + mCachedTasks.put(task.getId(), task); callback.onTaskLoaded(task); } @@ -230,6 +235,11 @@ public void onDataNotAvailable() { mTasksRemoteDataSource.getTask(taskId, new GetTaskCallback() { @Override public void onTaskLoaded(Task task) { + // Do in memory cache update to keep the app UI up to date + if (mCachedTasks == null) { + mCachedTasks = new LinkedHashMap<>(); + } + mCachedTasks.put(task.getId(), task); callback.onTaskLoaded(task); } diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksDbHelper.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksDbHelper.java index 93fda85bb..c73bfc432 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksDbHelper.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksDbHelper.java @@ -33,8 +33,7 @@ public class TasksDbHelper extends SQLiteOpenHelper { private static final String SQL_CREATE_ENTRIES = "CREATE TABLE " + TasksPersistenceContract.TaskEntry.TABLE_NAME + " (" + - TasksPersistenceContract.TaskEntry._ID + TEXT_TYPE + " PRIMARY KEY," + - TasksPersistenceContract.TaskEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP + + TasksPersistenceContract.TaskEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + " PRIMARY KEY," + TasksPersistenceContract.TaskEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP + TasksPersistenceContract.TaskEntry.COLUMN_NAME_DESCRIPTION + TEXT_TYPE + COMMA_SEP + TasksPersistenceContract.TaskEntry.COLUMN_NAME_COMPLETED + BOOLEAN_TYPE + diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksPersistenceContract.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksPersistenceContract.java index 0e73dca8d..669516a9c 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksPersistenceContract.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksPersistenceContract.java @@ -29,7 +29,7 @@ private TasksPersistenceContract() {} /* Inner class that defines the table contents */ public static abstract class TaskEntry implements BaseColumns { - public static final String TABLE_NAME = "task"; + public static final String TABLE_NAME = "tasks"; public static final String COLUMN_NAME_ENTRY_ID = "entryid"; public static final String COLUMN_NAME_TITLE = "title"; public static final String COLUMN_NAME_DESCRIPTION = "description"; diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsActivity.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsActivity.java index 0d60a0296..226cd7f3e 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsActivity.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/statistics/StatisticsActivity.java @@ -16,9 +16,9 @@ package com.example.android.architecture.blueprints.todoapp.statistics; -import android.content.Intent; import android.os.Bundle; import android.support.design.widget.NavigationView; +import android.support.v4.app.NavUtils; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; @@ -26,7 +26,6 @@ import android.view.MenuItem; import com.example.android.architecture.blueprints.todoapp.R; -import com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity; import com.example.android.architecture.blueprints.todoapp.util.ActivityUtils; import javax.inject.Inject; @@ -93,11 +92,7 @@ private void setupDrawerContent(NavigationView navigationView) { public boolean onNavigationItemSelected(MenuItem menuItem) { switch (menuItem.getItemId()) { case R.id.list_navigation_menu_item: - Intent intent = - new Intent(StatisticsActivity.this, TasksActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK); - startActivity(intent); + NavUtils.navigateUpFromSameTask(StatisticsActivity.this); break; case R.id.statistics_navigation_menu_item: // Do nothing, we're already on that screen diff --git a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksActivity.java b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksActivity.java index 420294f6e..15e306226 100644 --- a/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksActivity.java +++ b/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/tasks/TasksActivity.java @@ -110,8 +110,6 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { case R.id.statistics_navigation_menu_item: Intent intent = new Intent(TasksActivity.this, StatisticsActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); break; default: diff --git a/todoapp/app/src/main/res/layout/addtask_frag.xml b/todoapp/app/src/main/res/layout/addtask_frag.xml index 741409c31..159cc5555 100644 --- a/todoapp/app/src/main/res/layout/addtask_frag.xml +++ b/todoapp/app/src/main/res/layout/addtask_frag.xml @@ -32,8 +32,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/title_hint" - android:textAppearance="@style/TextAppearance.AppCompat.Title" - android:maxLines="1"/> + android:maxLines="1" + android:textAppearance="@style/TextAppearance.AppCompat.Title" /> mGetTaskCallbackCaptor; private AddEditTaskPresenter mAddEditTaskPresenter; + private Lazy mBooleanLazy = new Lazy() { + @Override + public Boolean get() { + return true; + } + }; @Before public void setupMocksAndView() { @@ -65,7 +73,7 @@ public void setupMocksAndView() { @Test public void saveNewTaskToRepository_showsSuccessMessageUi() { // Get a reference to the class under test - mAddEditTaskPresenter = new AddEditTaskPresenter("1", mTasksRepository); + mAddEditTaskPresenter = new AddEditTaskPresenter("1", mTasksRepository, mBooleanLazy); mAddEditTaskPresenter.takeView(mAddEditTaskView); // When the presenter is asked to save a task mAddEditTaskPresenter.saveTask("New Task Title", @@ -79,7 +87,7 @@ public void saveNewTaskToRepository_showsSuccessMessageUi() { @Test public void saveTask_emptyTaskShowsErrorUi() { // Get a reference to the class under test - mAddEditTaskPresenter = new AddEditTaskPresenter(null, mTasksRepository); + mAddEditTaskPresenter = new AddEditTaskPresenter(null, mTasksRepository, mBooleanLazy); mAddEditTaskPresenter.takeView(mAddEditTaskView); // When the presenter is asked to save an empty task @@ -92,7 +100,7 @@ public void saveTask_emptyTaskShowsErrorUi() { @Test public void saveExistingTaskToRepository_showsSuccessMessageUi() { // Get a reference to the class under test - mAddEditTaskPresenter = new AddEditTaskPresenter("1", mTasksRepository); + mAddEditTaskPresenter = new AddEditTaskPresenter("1", mTasksRepository, mBooleanLazy); mAddEditTaskPresenter.takeView(mAddEditTaskView); // When the presenter is asked to save an existing task @@ -108,7 +116,7 @@ public void populateTask_callsRepoAndUpdatesView() { Task testTask = new Task("TITLE", "DESCRIPTION"); // Get a reference to the class under test mAddEditTaskPresenter = new AddEditTaskPresenter(testTask.getId(), - mTasksRepository); + mTasksRepository, mBooleanLazy); //When we bind the view we will also populate the task mAddEditTaskPresenter.takeView(mAddEditTaskView); diff --git a/todoapp/build.gradle b/todoapp/build.gradle index d98f2a932..48c0144dc 100644 --- a/todoapp/build.gradle +++ b/todoapp/build.gradle @@ -32,7 +32,7 @@ task clean(type: Delete) { ext { // Sdk and tools minSdkVersion = 14 - targetSdkVersion = 25 + targetSdkVersion = 26 compileSdkVersion = 26 buildToolsVersion = '26.0.1' diff --git a/todoapp/gradle.properties b/todoapp/gradle.properties index 1d3591c8a..aeb64233b 100644 --- a/todoapp/gradle.properties +++ b/todoapp/gradle.properties @@ -15,4 +15,5 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true +org.gradle.jvmargs=-Xmx1536M