Skip to content

Commit

Permalink
Merge pull request #3 from JoseAlcerreca/merge_mike_todomvp
Browse files Browse the repository at this point in the history
Merges changes from todo-mvp and fixes UI tests
  • Loading branch information
digitalbuddha authored Aug 16, 2017
2 parents 3f9af6d + 2bf26a6 commit c7718a1
Show file tree
Hide file tree
Showing 23 changed files with 243 additions and 74 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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

Expand Down
26 changes: 26 additions & 0 deletions circle.yml
Original file line number Diff line number Diff line change
@@ -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/ \;
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public void onTasksLoaded(List<Task> tasks) {

boolean newTask1IdFound = false;
boolean newTask2IdFound = false;
for (Task task: tasks) {
for (Task task : tasks) {
if (task.getId().equals(newTask1.getId())) {
newTask1IdFound = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -83,38 +86,17 @@ 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()));
}

@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()));
Expand All @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -213,7 +214,6 @@ public void markTaskAsComplete() {

@Test
public void markTaskAsActive() {

viewAllTasks();

// Add completed task
Expand Down Expand Up @@ -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);

Expand Down
22 changes: 15 additions & 7 deletions todoapp/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
~ limitations under the License.
-->

<manifest
package="com.example.android.architecture.blueprints.todoapp"
xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.android.architecture.blueprints.todoapp">

<application
android:allowBackup="false"
Expand All @@ -27,16 +27,24 @@
android:theme="@style/AppTheme"
android:name="com.example.android.architecture.blueprints.todoapp.ToDoApplication">
<activity
android:name="com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity"
android:theme="@style/AppTheme.OverlapSystemBar">
android:name="com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity"
android:theme="@style/AppTheme.OverlapSystemBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.example.android.architecture.blueprints.todoapp.addedittask.AddEditTaskActivity" />
<activity android:name="com.example.android.architecture.blueprints.todoapp.statistics.StatisticsActivity" />
<activity android:name="com.example.android.architecture.blueprints.todoapp.taskdetail.TaskDetailActivity" />
<activity android:name="com.example.android.architecture.blueprints.todoapp.addedittask.AddEditTaskActivity" />
<activity
android:name="com.example.android.architecture.blueprints.todoapp.statistics.StatisticsActivity"
android:parentActivityName=".tasks.TasksActivity"
tools:ignore="UnusedAttribute">
<!-- Parent activity meta-data to support 4.0 and lower -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".tasks.TasksActivity" />
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 {
Expand All @@ -22,14 +23,15 @@ public class ToDoApplication extends DaggerApplication {

@Override
protected AndroidInjector<? extends DaggerApplication> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,7 @@ interface Presenter extends BasePresenter<View> {
void saveTask(String title, String description);

void populateTask();

boolean isDataMissing();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,4 @@ public void setDescription(String description) {
public boolean isActive() {
return isAdded();
}

}
Loading

0 comments on commit c7718a1

Please sign in to comment.