Advanced Android traineship

Program

  • android plateform tools
  • MVP simple project
    • Basic explanation
    • first project
    • Parse.com
    • First injection with ButterKnife
    • Dagger2
  • Create a real project (Chat)

Android studio

  • New android platform
  • Based by JetBrains' IntelliJ IDEA
  • Build system : Graddle
  • Support Google TV, Google Wear, Google Glasses, ...

but don't support NDK ...

MVC => MVP

MVC

Model - View - Controller

View

What the user see

Model

Business data, databases interaction, Http request, local storage, ...

Controller

It's the glue between View and Model. It map data to send to View. It control user interaction and may change model's data

Interaction

MVP

Model - View - Presenter

All presentation logic is pushed to presenter

View

The view is a passive interface that displays data (the model) and routes user commands (events) to the presenter to act upon that data.

Model

The model is an interface defining the data to be displayed or otherwise acted upon in the user interface.

Presenter

The presenter acts upon the model and the view. It retrieves data from repositories (the model), and formats it for display in the view.

The current activity or fragment

Business data, databases interaction, Http request, local storage, ...

The brain

public class MainActivity extends Activity implement ICustormerView
{
    CustomerPresenter mCustomerPresenter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mCustomerPresenter = new CustomerPresenter(this);
    }

MVP

Let's code

New project

Project name : First Mvp

Blank Activity

Activity Name : MainActivity

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true">

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="25dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="id"
                android:id="@+id/textView"
                android:layout_gravity="left" />

            <EditText
                android:layout_width="195dp"
                android:layout_height="wrap_content"
                android:inputType="number"
                android:ems="10"
                android:id="@+id/idText"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"
                android:layout_marginLeft="0dp"
                android:layout_marginRight="0dp" />
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="25dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="lastname"
                android:id="@+id/textView3" />

            <EditText
                android:layout_width="195dp"
                android:layout_height="wrap_content"
                android:inputType="text"
                android:ems="10"
                android:id="@+id/lastNameEditText"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"/>
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="25dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:text="firstname"
                android:id="@+id/textView2" />

            <EditText
                android:layout_width="195dp"
                android:layout_height="wrap_content"
                android:inputType="text"
                android:ems="10"
                android:id="@+id/firstNameEditText"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"/>
        </RelativeLayout>

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="100dp">

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Save"
                android:id="@+id/saveButton" />

            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Load"
                android:id="@+id/loadButton"
                android:layout_gravity="right"
                android:layout_alignParentRight="true"
                android:layout_alignParentEnd="true"/>
        </RelativeLayout>

    </LinearLayout>

</RelativeLayout>
private EditText mFirstNameEditText, mLastNameEditText, mIdEditText;
private Button mSaveButton, LoadButton;
CustomerPresenter mCustomerPresenter;

Add into MainActivity Class

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mIdEditText = (EditText)findViewById(R.id.idText);
        mFirstNameEditText = (EditText)findViewById(R.id.firstNameEditText);
        mLastNameEditText = (EditText)findViewById(R.id.lastNameEditText);

        mSaveButton = (Button)findViewById(R.id.saveButton);
        mLoadButton = (Button)findViewById(R.id.loadButton);

        mCustomerPresenter = new CustomerPresenter(this);
        mSaveButton.setOnClickListener(this);
        mLoadButton.setOnClickListener(this);
    }

Add into onCreate

    public CustomerPresenter(MainActivity mainActivity) {
    }

    public void saveCustomer (String firstName, String lastName) {
    }

    public void loadCustomer (int id) {
    }

Create customerPresenter

public class MainActivity extends Activity implements ICustomerView, View.OnClickListener {

Go back on MainActivity

    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.saveButton:
                mCustomerPresenter.saveCustomer(mFirstNameEditText.getText().toString(),
                        mLastNameEditText.getText().toString());
                break;
            case R.id.loadButton:
                String id = mIdEditText.getText().toString();
                if (id.isEmpty()) { Toast.makeText(this, "Need to enter id", Toast.LENGTH_LONG).show(); return ; }
                mCustomerPresenter.loadCustomer(Integer.parseInt(mIdEditText.getText().toString()));
                break;
        }
    }
public interface ICustomerView {
    void setLastName (String lastName);
    void setFirstName (String firstName);
    void setId(Integer id);
}

Let's create ICustomerView

And create implement methods on MainActivity

    @Override
    public void setLastName(String lastName) {
        mLastNameEditText.setText(lastName);
    }

    @Override
    public void setFirstName(String firstName) {
        mFirstNameEditText.setText(firstName);
    }

    @Override
    public void setId(Integer id) {
        mIdEditText.setText(id.toString());
    }
public class CustomerPresenter {
    private ICustomerView mCustomerView;
    private ICustomerModel mCustomerModel;

    public CustomerPresenter(ICustomerView view) {
        mCustomerView = view;
        mCustomerModel = new CustomerModel();
    }

    public CustomerPresenter(MainActivity mainActivity) {
        mCustomerView = (ICustomerView)mainActivity;
        mCustomerModel = new CustomerModel();
    }

    public void saveCustomer (String firstName, String lastName) {
        mCustomerModel.setFirstName(firstName);
        mCustomerModel.setLastName(lastName);
        mCustomerModel.save();
    }

    public void loadCustomer (int id) {
        mCustomerModel.load(id);
        mCustomerView.setId(mCustomerModel.getId());
        mCustomerView.setFirstName(mCustomerModel.getFirstName());
        mCustomerView.setLastName(mCustomerModel.getLastName());
    }

Replace the CustomerPresenter

public interface ICustomerModel {
    void setFirstName(String firstName);
    void setLastName(String lastName);
    void setId(Integer id);
    String getFirstName();
    String getLastName();
    Integer getId();
    ICustomerModel load(Integer id);
    void save();
}

Last but not the least, the Model !

ICustomerModel

public class CustomerModel implements ICustomerModel {
    String firstName;
    String lastName;
    Integer id;

    public CustomerModel() {
    }

    public CustomerModel(Integer id) {
    }

    @Override
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @Override
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Override
    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String getFirstName() {
        return firstName;
    }

    @Override
    public String getLastName() {
        return lastName;
    }

    @Override
    public Integer getId() {
        return id;
    }

    @Override
    public ICustomerModel load(Integer id) {
        // read on db

        this.id = 1;
        firstName = "first";
        lastName  = "last";
        return null;
    }

    @Override
    public void save() {
        // write on db
    }
}

CustomerModel

Running MVP

Parse

No more backend developpement

What parse can do ?

  • Databases
  • ACL
  • Notification
  • Analytics

Running on many platform

Even physical

Running on many language

  • OSX
  • Windows
  • Javascript
  • Unity
  • Php
  • .Net

Cost ? It's free ! ... until

  • 30 requests per seconds
  • 1 000 000 first push notification
  • Analytics, unlimited free

More over

  • Really easy to use
  • No backend dev
  • No server to manage
  • Free until your application is really used

Parse

Let's dev

First create an account

... and "sign up for free"

Create your application

You suppose to be on the dashboard

  • Select "Data"
  • Select "Mobile"
  • Select "Android"
  • Select "Native (Java)"
  • Select "Existing project"

Download the SDK

Download the SDK, and put all file on app/libs

Add permission

And permission on AndroidManifest.xml, on <manifest></manifest>

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

And add parse's init

Create new class CustomerApplication.java

public class CustomerApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        Parse.enableLocalDatastore(this);
        Parse.initialize(this,  getString(R.string.parse_app_id), getString(R.string.parse_client_key));
        ParseObject.registerSubclass(Customer.class);
    }
}

On strings.xml, add ...

    <string name="parse_app_id">xCoCH4Ct98vUSr8wpBp3Ej9riw5nVM4qx9oi76nw</string>
    <string name="parse_client_key">WUqycIkrmBjtbgMtQT939yuaxNtMzAtfv1Uuxd0t</string>

Done, parse is set and up, let's use it

On acitvity_main, change for ...

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" 
        android:name=".CustomerApplication">

Save customers

We want to display the objectId on idText

  • The user clic on save
  • The view catch the click and call the presenter
  • The presenter call the model
  • The model communicate with parse

Let's return objectId

  • Model callback presenter
  • Presenter call the view
  • ObjectId is displayed on idText

CustomerPresenter.java

    public void saveCustomer (String firstName, String lastName) {
        mCustomerModel.setFirstName(firstName);
        mCustomerModel.setLastName(lastName);
        mCustomerModel.save(new CustomerModelSaveCallback() {
            @Override
            public void done(CustomerModel customer) {
                mCustomerView.setId(customer.getId());
            }
        });
    }

CustomerModelSaveCallback

public interface CustomerModelSaveCallback {
    void done(CustomerModel customer);
}

ICustomerModel

    void save(CustomerModelSaveCallback callback);

CustomerModel

    @Override
    public void save(final CustomerModelSaveCallback callback) {
        customer.setFirstName(firstName);
        customer.setLastName(lastName);
        customer.saveInBackground(new SaveCallback() {
            @Override
            public void done(ParseException e) {
                self.setId(customer.getObjectId());
                callback.done(self);
            }
        });
    }

That's all

Code : load

CustomerModelLoadCallback

public interface CustomerModelLoadCallback {
    void done(CustomerModel customer);
}

CustomerPresenter

    public void loadCustomer (String id) {
        mCustomerModel.load(id, new CustomerModelLoadCallback() {
            @Override
            public void done(CustomerModel customer) {
                mCustomerView.setId(customer.getId());
                mCustomerView.setFirstName(customer.getFirstName());
                mCustomerView.setLastName(customer.getLastName());
            }
        });
    }

ICustomerModel

void load(String id, CustomerModelLoadCallback callback);

CustomerModel

    @Override
    public void load(String id, final CustomerModelLoadCallback callback) {
        ParseQuery<Customer> query = ParseQuery.getQuery(Customer.class);
        query.whereContains("objectId", id);
        query.findInBackground(new FindCallback<Customer>() {
            @Override
            public void done(List<Customer> list, ParseException e) {
                self.customer = list.get(0);
                self.setId(self.customer.getObjectId());
                self.setFirstName(self.customer.getFirstName());
                self.setLastName(self.customer.getLastName());
                callback.done(self);
            }
        });
    }

Code injection

Dagger / Butterknife

Code injection is done on compiling ...

Thing you should know

... so dependency and initialisation are checked on compilation 

Module injection are done with singleton ...

... so you can get you modules every where in your code

You can inject different module following you environement ...

... one module for production using real API

... one module for developpement using fileDB

ButterKnife

View "injection" library for Android

Annotate fields with @InjectView and a view ID

class ExampleActivity extends Activity {
  @InjectView(R.id.title) TextView title;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.simple_activity);
    ButterKnife.inject(this);
    
  }
}

Listener injection

@OnClick(R.id.submit)
public void submit(View view) {
  // TODO submit data to server...
}

Argument are optional

@OnClick(R.id.submit)
public void submit() {
  // TODO submit data to server...
}

Select multiple IDs

@OnClick({ R.id.door1, R.id.door2, R.id.door3 })
public void pickDoor(DoorView door) {
  if (door.hasPrizeBehind()) {
    Toast.makeText(this, "You win!", LENGTH_SHORT).show();
  } else {
    Toast.makeText(this, "Try again", LENGTH_SHORT).show();
  }
}

And more ...

@InjectViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List<EditText> nameViews;

View list

ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);

And apply action on them

@Optional @InjectView(R.id.might_not_be_there) TextView mightNotBeThere;

By default, if target is not exist on the view, ButterKnife will throw an error. To suppress this behavior : @Optional

@OnItemSelected(R.id.list_view)
void onItemSelected(int position) {
  // TODO ...
}

Event on list

ButterKnife

Let's code

Gradle configuration

compile 'com.jakewharton:butterknife:6.1.0'

Dagger

Dagger keyword

  • @Inject
  • @Module
  • @Provide
  • @Component

@Inject

We indicate to dagger we request this depedency injection. Dagger will construct instances of this annitated calsses and satisfy their dependencies.

@Module

Modules are classes whose methods provide dependencies, so we define a class and annotate it with @Module

@Provide

Inside modules we define methods containing this annotation which tells Dagger how we want to construct and provide those mentioned dependencies.

@Module

Components basically are injectors, its main responsibility is to put together @Inject and @Module

@Scope

Permit to define boundaries to injection. In other words, we can define the granularity of your scopes (@PerFragment, @PerUser, etc).

@Qualifier

Define context @ForApplication, @ForActivity

And more, there is @Scope, @Qualifier

Dagger2

Let's code

Dagger

Configuration

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.meuuh.test.firstmvp"
        minSdkVersion 16
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile 'com.parse.bolts:bolts-android:1.+'
    compile fileTree(dir: 'libs', include: 'Parse-*.jar')

    compile 'com.jakewharton:butterknife:6.1.0'

    compile 'com.android.support:appcompat-v7:22.1.1'

    compile 'com.google.dagger:dagger:2.0'
    apt 'com.google.dagger:dagger-compiler:2.0'
    provided 'org.glassfish:javax.annotation:10.0-b28'
}
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.2.2'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}

allprojects {
    repositories {
        mavenCentral()
        maven{
            url 'https://oss.sonatype.org/content/repositories/snapshots/'
        }
    }
}

build.gradle (Project)

build.gradle (Module)

Inject presenter

@Module
public class CustomerPresenterModule {
    @Provides
    @Singleton
    CustomerPresenter provideCustomerPresenter() {
        return new CustomerPresenter();
    }
}

CustomerPresenterModule.java

@Singleton
@Component(modules = {CustomerPresenterModule.class})
public interface CustomerPresenterComponent {
    CustomerPresenter provideCustomerPresenter();
}

CustomerPresenterComponent.java

@Inject CustomerPresenter mCustomerPresenter;

MainActivity

How and where to use it ?

Create Chat Application

Tests

Unit test

Unit testing is a procedure that checks a specific piece of software is abstracted from everything else in the application

Integration test

The integration test consolidates several changes to check if there are any errors during compilation of all these changes. (Jenkins, git)

Functional test

The functional test is a user scenario applied to a application screen. It simulates user interactions and audits on the data displayed on the graphical components that interfaces after testing.

Acceptance test

The acceptance test is designed to ensure that the project complies with the specifications. This step involves the presence of a work of mastery (entity providing technical expertise as required) and a project owner (carrier entity needed) by performing procedures of functional and technical tests.

(human)

Android studio

Android studio manage unit test by default. 

  • Class need to be ended by xxxTests
  • Class need to be public
  • Assert on return classes (not really easy with callback)

Unit test

How it work ?

//mock creation
 List mockedList = mock(List.class);

 //using mock object
 mockedList.add("one");
 mockedList.clear();

 //verification
 verify(mockedList).add("one");
 verify(mockedList).clear();

Mockito

Warehouse project

New project

Project name : Warehouse

Blank Activity

Activity Name : MainActivity

Module: app

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "com.meuuh.test.myapplication"
        minSdkVersion 19
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    packagingOptions {
        exclude 'LICENSE.txt'
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/ASL2.0'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/LICENSE'
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:22.1.1'

    androidTestCompile 'org.mockito:mockito-core:1.9.5'
    androidTestCompile 'com.google.dexmaker:dexmaker:1.0'
    androidTestCompile 'com.google.dexmaker:dexmaker-mockito:1.0'
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
    androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
}

Warehouse.java

public interface Warehouse {
    boolean hasInventory(String product, int quantity);
    void remove(String product, int quantity);
}

RealWarehouse.java

public interface Warehouse {
    boolean hasInventory(String product, int quantity);
    void remove(String product, int quantity);
}public class RealWarehouse implements Warehouse{
    public RealWarehouse() {
        products = new HashMap();
        products.put("Talisker", 5);
        products.put("Lagavulin", 2);
    }

    public boolean hasInventory(String product, int quantity) {
        return inStock(product) >= quantity;
    }

    public void remove(String product, int quantity) {
        products.put(product, inStock(product) - quantity);
    }

    private Integer inStock(String product) {
        Integer quantity = (int)products.get(product);
        return quantity == null ? 0 : quantity;
    }

    private HashMap products;
}

Order.java

public class Order {
    public Order(String product, int quantity) {
        this.product = product;
        this.quantity = quantity;
    }

    public void fill(Warehouse warehouse) {
        if (warehouse.hasInventory(product, quantity)) {
            warehouse.remove(product, quantity);
            filled = true;
        }
    }

    public boolean isFilled() {
        return filled;
    }

    private boolean filled = false;
    private String product;
    private int quantity;
}

MainActivity

public class MainActivity extends ActionBarActivity {

    private Warehouse warehouse = new RealWarehouse();

    private EditText productEditText;
    private EditText quantityEditText;


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        productEditText = (EditText)findViewById(R.id.product);
        quantityEditText = (EditText)findViewById(R.id.quantity);
    }

    public void placeOrder(View view) {
        String product = productEditText.getText().toString();
        int quantity = Integer.parseInt(quantityEditText.getText().toString());
        Order order = new Order(product, quantity);
        order.fill(warehouse);

        String message = order.isFilled() ? "Success" : "Failure";
        Toast toast = Toast.makeText(this, message, Toast.LENGTH_SHORT);
        toast.show();
    }
}

The mock : MainActivityTests

public class MainActivityTests extends ActivityInstrumentationTestCase2<MainActivity> {
    public MainActivityTests() {
        super(MainActivity.class);
    }

    public void testInStock() {
        Warehouse mockWarehouse = mock(Warehouse.class);

        when(mockWarehouse.hasInventory("Talisker", 50)).thenReturn(true);

        Order order = new Order("Talisker", 50);
        order.fill(mockWarehouse);

        assertTrue(order.isFilled());
        verify(mockWarehouse).remove("Talisker", 50);
    }

    public void testOutOfStock() {
        Warehouse mockWarehouse = mock(Warehouse.class);

        when(mockWarehouse.hasInventory("Talisker", 50)).thenReturn(false);

        Order order = new Order("Talisker", 50);
        order.fill(mockWarehouse);

        assertFalse(order.isFilled());
    }
}

Now we can create run the test

What about documentation ?

What about callback ?

Functional test

Espresso

Entry point to interactions with views (via onView and onData). Also exposes APIs that are not necessarily tied to any view (e.g. pressBack).

Basics

ViewMatchers

You can pass one or more of these to the onView method to locate a view within the current view hierarchy

ViewActions

A collection of ViewActions that can be passed to the ViewInteraction.perform method (for example, click).

ViewAssertions

The matches assertion, which uses a View matcher to assert the state of the currently selected view

Example

Espresso.onView(ViewMatchers.withId(R.id.my_view))            // withId(R.id.my_view) is a ViewMatcher
  .perform(ViewActions.click())                               // click() is a ViewAction
  .check(ViewAssertions.matches(ViewMatchers.isDisplayed())); // matches(isDisplayed()) is a ViewAssertion

Click

onView(withId(R.id.button)).perform(click());

Check text

onView(withId(R.id.spinnertext_simple)).check(matches(withText(containsString("Americano"))));

Perform an action

onView(...).perform(click());

Perform an assertion

onView(...).check(matches(withText("Hello!")));

Click on list (Adapter)

onData(allOf(is(instanceOf(String.class)), is("Americano"))).perform(click());

Documentation

Espresso

On ChatApp

MainActivityTests

public class MainActivityTests extends ActivityInstrumentationTestCase2<MainActivity> {
    public static String uuid = null;
    public static String username;
    public static String password;
    public static String pseudo;

    public MainActivityTests() {
        super(MainActivity.class);
        ParseUser user = ParseUser.getCurrentUser();
        if (user != null) user.logOut();

        if (this.uuid == null) {
            this.uuid = UUID.randomUUID().toString().substring(0, 8);
            this.username = this.uuid + "@meuuh.com";
            this.password = this.uuid;
            this.pseudo = "test_" + this.uuid;
        }
    }

    @Override
    public void setUp() throws Exception {
        super.setUp();
        getActivity();
    }

    public void testCreateLogin() throws InterruptedException {
    Espresso.onView(ViewMatchers.withId(R.id.parse_login)).check(ViewAssertions.matches(ViewMatchers.isDisplayed()));

    Espresso.onView(ViewMatchers.withId(R.id.parse_signup_button)).perform(ViewActions.click());
    /**/

    }
}

Template

Java

ITemplate

public class ImplementDog implements ITemplate<Dog> {
    Dog dog;

    public ImplementDog(Dog dog) {
        this.dog = dog;
    }

    @Override
    public void call() {
        dog.doIt();
    }

    @Override
    public void init(Dog object) {

    }

    @Override
    public Dog returnObject() {
        return null;
    }
}

DogImplementation

public interface ITemplate<T> {
    public void init(T object);
    public void call();
    public T returnObject();
}
public class Dog {
    public void doIt() {
        Log.e("test_template", "I'm a dog");
    }
}

Dog class

public class ImplementCat implements ITemplate<Cat> {
    Cat cat;

    public ImplementCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public void init(Cat object) {

    }

    @Override
    public Cat returnObject() {
        return null;
    }

    @Override
    public void call() {
        cat.doIt();
    }
}

CatImplementation

public class Cat {
    public void doIt() {
        Log.e("test_template", "I'm a cat");
    }
}

Cat class

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ImplementDog implementDog = new ImplementDog(new Dog());
        implementDog.call();

        ImplementCat implementCat = new ImplementCat(new Cat());
        implementCat.call();
    }

MainActivity

Source

Made with Slides.com