提交 6419d4c5 编写于 作者: J Jose Alcérreca 提交者: Florina Muntenescu

Adds Room to todo-mvvm-live and updates versions (#459)

* Update todo-mvp README.md

Refactored the content around a new structure, with new standard titles used for each sample
Fixed various grammar issues
Applied formatting
Additional explanations and links where appropriate
Introduced pre-requisite topics

* Update README.md

Removed a redundant sentence.

* copies Firebase Test Lab results to CIRCLE_TEST_REPORTS

* Replaced deprecated attribute

* Bumps versions and fixes annotation imports

* Fixes toolbar title not being persisted after rotation

* Fixes navigation issue #287

* Bumps circle CI Android dependencies

* Fixes comment in test

* Bumps versions in travis.yml

* Moving the setting of the AddEditTaskPresenter in the constructor.
Tests added to check that the presenter is set to the view.

* Removing the `_id` column and making `entryid` primary key.
Moved the `TasksLocalDataSourceTest` to correct package.

* First commit: MPV with Room

* Readme updated

* Readme updated

* Fixing CI

* Fixing tests for local repository

* Using Idling resources for tests, for commands that are executed on the background thread

* comments updated

* Fixed Firebase Test Lab script

* fix typo in androidTest

* Fix typo (#415)

- Change "compliment" to "complement"

* Fixed the typo (#425)

* Tries to fix circle adding google repo

* Buildtools 26 is mandatory now, apparently

* More CI fixes

* CI caught some merge errors + circle fix

* Updates todo-mvvm-databinding with changes from todo-mvp (#437)

* Update todo-mvp README.md

Refactored the content around a new structure, with new standard titles used for each sample
Fixed various grammar issues
Applied formatting
Additional explanations and links where appropriate
Introduced pre-requisite topics

* Update README.md

Removed a redundant sentence.

* copies Firebase Test Lab results to CIRCLE_TEST_REPORTS

* Replaced deprecated attribute

* Bumps versions and fixes annotation imports

* Fixes toolbar title not being persisted after rotation

* Fixes navigation issue #287

* Bumps circle CI Android dependencies

* Fixes comment in test

* Bumps versions in travis.yml

* Moving the setting of the AddEditTaskPresenter in the constructor.
Tests added to check that the presenter is set to the view.

* Removing the `_id` column and making `entryid` primary key.
Moved the `TasksLocalDataSourceTest` to correct package.

* Fixed Firebase Test Lab script

* Tries to fix circle adding google repo

* Buildtools 26 is mandatory now, apparently

* More CI fixes

* CI caught some merge errors + circle fix

* Fixed Firebase Test Lab script (#440)

* Updates room branch to API 26, Espresso, Arch Components

* Adds google m2 repository

* Adding google() to repositories

* Reverts to todo-mvp's README

* No need for LicecycleActivities now
上级 3233f6e4
......@@ -6,6 +6,7 @@ android:
- build-tools-26.0.2
- android-26
- extra-android-m2repository
- extra-google-m2repository
jdk:
- oraclejdk8
script:
......
......@@ -2,8 +2,6 @@
This version of the app is called todo-mvvm-live, and it uses some Architecture Components like ViewModel, LiveData, and other lifecycle-aware classes. It's based on the [todo-mvvm-databinding](https://github.com/googlesamples/android-architecture/tree/todo-mvvm-databinding/) sample, which uses the [Data Binding Library](http://developer.android.com/tools/data-binding/guide.html#data_objects) to display data and bind UI elements to actions.
This sample is not final, as the Architecture Components are in alpha stage at the time of writing this document.
## What you need
Before exploring this sample, you should familiarize yourself with the following topics:
......
......@@ -7,7 +7,7 @@ machine:
dependencies:
pre:
- sudo pip install -U crcmod
- echo y | android update sdk --no-ui --all --filter "tools,platform-tools,build-tools-26.0.2,android-26,extra-android-m2repository"
- echo y | android update sdk --no-ui --all --filter "tools,platform-tools,build-tools-26.0.2,android-26,extra-android-m2repository,extra-google-m2repository"
post:
- cd todoapp;./gradlew :app:assembleDebug -PdisablePreDex
......
......@@ -5,5 +5,6 @@ build
.gradle
# Eclipse project files
.project
.settings/
.settings/
.classpath
.DS_Store
......@@ -45,9 +45,9 @@ android {
// Remove mockRelease as it's not needed.
android.variantFilter { variant ->
if(variant.buildType.name == 'release'
if (variant.buildType.name == 'release'
&& variant.getFlavors().get(0).name == 'mock') {
variant.setIgnore(true);
variant.setIgnore(true)
}
}
......@@ -76,6 +76,9 @@ dependencies {
compile "com.android.support:support-v4:$rootProject.supportLibraryVersion"
compile "com.google.guava:guava:$rootProject.guavaVersion"
// Architecture Components
compile "android.arch.persistence.room:runtime:$rootProject.roomVersion"
annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion"
compile "android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion"
annotationProcessor "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion"
......@@ -90,6 +93,8 @@ dependencies {
androidTestCompile "com.android.support.test:runner:$rootProject.ext.runnerVersion"
androidTestCompile "com.android.support.test:rules:$rootProject.ext.rulesVersion"
androidTestCompile "android.arch.persistence.room:testing:$rootProject.roomVersion"
// Dependencies for Android unit tests
androidTestCompile "junit:junit:$rootProject.ext.junitVersion"
androidTestCompile "org.mockito:mockito-core:$rootProject.ext.mockitoVersion"
......
......@@ -27,4 +27,4 @@
-dontwarn org.hamcrest.**
-dontwarn com.squareup.javawriter.JavaWriter
# Uncomment this if you use Mockito
-dontwarn org.mockito.**
\ No newline at end of file
-dontwarn org.mockito.**
/*
* Copyright 2017, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.architecture.blueprints.todoapp.data.source.local;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import android.arch.persistence.room.Room;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import com.example.android.architecture.blueprints.todoapp.data.Task;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.List;
@RunWith(AndroidJUnit4.class)
public class TasksDaoTest {
private static final Task TASK = new Task("title", "description", "id", true);
private ToDoDatabase mDatabase;
@Before
public void initDb() {
// using an in-memory database because the information stored here disappears when the
// process is killed
mDatabase = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),
ToDoDatabase.class).build();
}
@After
public void closeDb() {
mDatabase.close();
}
@Test
public void insertTaskAndGetById() {
// When inserting a task
mDatabase.taskDao().insertTask(TASK);
// When getting the task by id from the database
Task loaded = mDatabase.taskDao().getTaskById(TASK.getId());
// The loaded data contains the expected values
assertTask(loaded, "id", "title", "description", true);
}
@Test
public void insertTaskReplacesOnConflict() {
//Given that a task is inserted
mDatabase.taskDao().insertTask(TASK);
// When a task with the same id is inserted
Task newTask = new Task("title2", "description2", "id", true);
mDatabase.taskDao().insertTask(newTask);
// When getting the task by id from the database
Task loaded = mDatabase.taskDao().getTaskById(TASK.getId());
// The loaded data contains the expected values
assertTask(loaded, "id", "title2", "description2", true);
}
@Test
public void insertTaskAndGetTasks() {
// When inserting a task
mDatabase.taskDao().insertTask(TASK);
// When getting the tasks from the database
List<Task> tasks = mDatabase.taskDao().getTasks();
// There is only 1 task in the database
assertThat(tasks.size(), is(1));
// The loaded data contains the expected values
assertTask(tasks.get(0), "id", "title", "description", true);
}
@Test
public void updateTaskAndGetById() {
// When inserting a task
mDatabase.taskDao().insertTask(TASK);
// When the task is updated
Task updatedTask = new Task("title2", "description2", "id", true);
mDatabase.taskDao().updateTask(updatedTask);
// When getting the task by id from the database
Task loaded = mDatabase.taskDao().getTaskById("id");
// The loaded data contains the expected values
assertTask(loaded, "id", "title2", "description2", true);
}
@Test
public void updateCompletedAndGetById() {
// When inserting a task
mDatabase.taskDao().insertTask(TASK);
// When the task is updated
mDatabase.taskDao().updateCompleted(TASK.getId(), false);
// When getting the task by id from the database
Task loaded = mDatabase.taskDao().getTaskById("id");
// The loaded data contains the expected values
assertTask(loaded, TASK.getId(), TASK.getTitle(), TASK.getDescription(), false);
}
@Test
public void deleteTaskByIdAndGettingTasks() {
//Given a task inserted
mDatabase.taskDao().insertTask(TASK);
//When deleting a task by id
mDatabase.taskDao().deleteTaskById(TASK.getId());
//When getting the tasks
List<Task> tasks = mDatabase.taskDao().getTasks();
// The list is empty
assertThat(tasks.size(), is(0));
}
@Test
public void deleteTasksAndGettingTasks() {
//Given a task inserted
mDatabase.taskDao().insertTask(TASK);
//When deleting all tasks
mDatabase.taskDao().deleteTasks();
//When getting the tasks
List<Task> tasks = mDatabase.taskDao().getTasks();
// The list is empty
assertThat(tasks.size(), is(0));
}
@Test
public void deleteCompletedTasksAndGettingTasks() {
//Given a completed task inserted
mDatabase.taskDao().insertTask(TASK);
//When deleting completed tasks
mDatabase.taskDao().deleteCompletedTasks();
//When getting the tasks
List<Task> tasks = mDatabase.taskDao().getTasks();
// The list is empty
assertThat(tasks.size(), is(0));
}
private void assertTask(Task task, String id, String title,
String description, boolean completed) {
assertThat(task, notNullValue());
assertThat(task.getId(), is(id));
assertThat(task.getTitle(), is(title));
assertThat(task.getDescription(), is(description));
assertThat(task.isCompleted(), is(completed));
}
}
......@@ -16,12 +16,24 @@
package com.example.android.architecture.blueprints.todoapp.data.source.local;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyList;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.arch.persistence.room.Room;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;
import com.example.android.architecture.blueprints.todoapp.data.Task;
import com.example.android.architecture.blueprints.todoapp.data.source.TasksDataSource;
import com.example.android.architecture.blueprints.todoapp.util.SingleExecutors;
import org.junit.After;
import org.junit.Before;
......@@ -30,18 +42,8 @@ import org.junit.runner.RunWith;
import java.util.List;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyList;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
/**
* Integration test for the {@link TasksDataSource}, which uses the {@link TasksDbHelper}.
* Integration test for the {@link TasksDataSource}.
*/
@RunWith(AndroidJUnit4.class)
@LargeTest
......@@ -55,15 +57,25 @@ public class TasksLocalDataSourceTest {
private TasksLocalDataSource mLocalDataSource;
private ToDoDatabase mDatabase;
@Before
public void setup() {
mLocalDataSource = TasksLocalDataSource.getInstance(
InstrumentationRegistry.getTargetContext());
// using an in-memory database for testing, since it doesn't survive killing the process
mDatabase = Room.inMemoryDatabaseBuilder(InstrumentationRegistry.getContext(),
ToDoDatabase.class)
.build();
TasksDao tasksDao = mDatabase.taskDao();
// Make sure that we're not keeping a reference to the wrong instance.
TasksLocalDataSource.clearInstance();
mLocalDataSource = TasksLocalDataSource.getInstance(new SingleExecutors(), tasksDao);
}
@After
public void cleanUp() {
mLocalDataSource.deleteAllTasks();
mDatabase.close();
TasksLocalDataSource.clearInstance();
}
@Test
......@@ -170,7 +182,7 @@ public class TasksLocalDataSourceTest {
mLocalDataSource.getTask(newTask2.getId(), callback2);
verify(callback2).onDataNotAvailable();
verify(callback2, never()).onTaskLoaded(newTask1);
verify(callback2, never()).onTaskLoaded(newTask2);
mLocalDataSource.getTask(newTask3.getId(), callback3);
......
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.architecture.blueprints.todoapp.util;
import android.support.annotation.NonNull;
import com.example.android.architecture.blueprints.todoapp.util.AppExecutors;
import java.util.concurrent.Executor;
/**
* Allow instant execution of tasks.
*/
public class SingleExecutors extends AppExecutors {
private static Executor instant = new Executor() {
@Override
public void execute(@NonNull Runnable command) {
command.run();
}
};
public SingleExecutors() {
super(instant, instant, instant);
}
}
......@@ -20,7 +20,7 @@ import android.content.Intent;
import android.content.res.Resources;
import android.support.annotation.Nullable;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.IdlingRegistry;
import android.support.test.espresso.Espresso;
import android.support.test.espresso.intent.rule.IntentsTestRule;
import android.support.test.espresso.matcher.BoundedMatcher;
import android.support.test.filters.LargeTest;
......@@ -81,7 +81,15 @@ public class AddEditTaskScreenTest {
*/
@Before
public void registerIdlingResource() {
IdlingRegistry.getInstance().register(EspressoIdlingResource.getIdlingResource());
Espresso.registerIdlingResources(EspressoIdlingResource.getIdlingResource());
}
/**
* Unregister your Idling Resource so it can be garbage collected and does not leak any memory.
*/
@After
public void unregisterIdlingResource() {
Espresso.unregisterIdlingResources(EspressoIdlingResource.getIdlingResource());
}
@Test
......@@ -169,12 +177,4 @@ public class AddEditTaskScreenTest {
}
};
}
/**
* Unregister your Idling Resource so it can be garbage collected and does not leak any memory.
*/
@After
public void unregisterIdlingResource() {
IdlingRegistry.getInstance().unregister(EspressoIdlingResource.getIdlingResource());
}
}
package com.example.android.architecture.blueprints.todoapp.util;
import android.support.annotation.NonNull;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Executor that runs a task on a new background thread.
* This implementation is used by the Android instrumentation tests.
*/
public class DiskIOThreadExecutor implements Executor {
private final Executor mDiskIO;
public DiskIOThreadExecutor() {
mDiskIO = Executors.newSingleThreadExecutor();
}
@Override
public void execute(@NonNull Runnable command) {
// increment the idling resources before executing the long running command
EspressoIdlingResource.increment();
mDiskIO.execute(command);
// decrement the idling resources once executing the command has been finished
EspressoIdlingResource.decrement();
}
}
/*
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.android.architecture.blueprints.todoapp;
import android.arch.lifecycle.LifecycleRegistry;
import android.arch.lifecycle.LifecycleRegistryOwner;
import android.support.v7.app.AppCompatActivity;
/**
* Temporary class until Architecture Components is final. Makes {@link AppCompatActivity} a
* {@link LifecycleRegistryOwner}.
*/
public class LifecycleAppCompatActivity extends AppCompatActivity
implements LifecycleRegistryOwner {
private final LifecycleRegistry mRegistry = new LifecycleRegistry(this);
@Override
public LifecycleRegistry getLifecycle() {
return mRegistry;
}
}
......@@ -23,9 +23,9 @@ import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import com.example.android.architecture.blueprints.todoapp.LifecycleAppCompatActivity;
import com.example.android.architecture.blueprints.todoapp.R;
import com.example.android.architecture.blueprints.todoapp.ViewModelFactory;
import com.example.android.architecture.blueprints.todoapp.util.ActivityUtils;
......@@ -33,7 +33,7 @@ import com.example.android.architecture.blueprints.todoapp.util.ActivityUtils;
/**
* Displays an add or edit task screen.
*/
public class AddEditTaskActivity extends LifecycleAppCompatActivity implements AddEditTaskNavigator {
public class AddEditTaskActivity extends AppCompatActivity implements AddEditTaskNavigator {
public static final int REQUEST_CODE = 1;
......
......@@ -16,11 +16,11 @@
package com.example.android.architecture.blueprints.todoapp.addedittask;
import android.arch.lifecycle.LifecycleFragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
......@@ -35,7 +35,7 @@ import com.example.android.architecture.blueprints.todoapp.util.SnackbarUtils;
/**
* Main UI for the add task screen. Users can enter a task title and description.
*/
public class AddEditTaskFragment extends LifecycleFragment {
public class AddEditTaskFragment extends Fragment {
public static final String ARGUMENT_EDIT_TASK_ID = "EDIT_TASK_ID";
......
......@@ -16,6 +16,10 @@
package com.example.android.architecture.blueprints.todoapp.data;
import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.Ignore;
import android.arch.persistence.room.PrimaryKey;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
......@@ -27,18 +31,24 @@ import java.util.UUID;
/**
* Immutable model class for a Task.
*/
@Entity(tableName = "tasks")
public final class Task {
@PrimaryKey
@NonNull
@ColumnInfo(name = "entryid")
private final String mId;
@Nullable
@ColumnInfo(name = "title")
private final String mTitle;
@Nullable
@ColumnInfo(name = "description")
private final String mDescription;
private boolean mCompleted;
@ColumnInfo(name = "completed")
private final boolean mCompleted;
/**
* Use this constructor to create a new active Task.
......@@ -46,6 +56,7 @@ public final class Task {
* @param title title of the task
* @param description description of the task
*/
@Ignore
public Task(@Nullable String title, @Nullable String description) {
this(title, description, UUID.randomUUID().toString(), false);
}
......@@ -58,6 +69,7 @@ public final class Task {
* @param description description of the task
* @param id id of the task
*/
@Ignore
public Task(@Nullable String title, @Nullable String description, @NonNull String id) {
this(title, description, id, false);