├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable-mdpi │ │ │ │ ├── octocat.png │ │ │ │ ├── profile.jpg │ │ │ │ ├── placeholder.png │ │ │ │ └── ic_search_white_36dp.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── octocat.png │ │ │ │ ├── placeholder.png │ │ │ │ └── ic_search_white_36dp.png │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-hdpi │ │ │ │ └── ic_search_white_36dp.png │ │ │ ├── drawable-xhdpi │ │ │ │ └── ic_search_white_36dp.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ └── ic_search_white_36dp.png │ │ │ ├── values-v21 │ │ │ │ └── styles.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ ├── layout │ │ │ │ ├── activity_search_user.xml │ │ │ │ ├── activity_user_details.xml │ │ │ │ ├── view_progressbar.xml │ │ │ │ ├── fragment_user_details.xml │ │ │ │ ├── item_user.xml │ │ │ │ ├── fragment_search_user.xml │ │ │ │ └── item_repo.xml │ │ │ ├── menu │ │ │ │ └── menu_main.xml │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ ├── colors.xml │ │ │ │ └── styles.xml │ │ │ └── layout-large │ │ │ │ └── activity_search_user.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── hugo │ │ │ │ └── mvpsampleapplication │ │ │ │ ├── utils │ │ │ │ ├── ThreadExecutor.java │ │ │ │ ├── PostExecutionThread.java │ │ │ │ ├── dependencyinjection │ │ │ │ │ ├── PerActivity.java │ │ │ │ │ ├── components │ │ │ │ │ │ ├── UserComponent.java │ │ │ │ │ │ └── ApplicationComponent.java │ │ │ │ │ └── modules │ │ │ │ │ │ ├── UserModule.java │ │ │ │ │ │ └── ApplicationModule.java │ │ │ │ ├── UiThread.java │ │ │ │ └── JobExecutor.java │ │ │ │ ├── features │ │ │ │ ├── Presenter.java │ │ │ │ ├── DefaultSubscriber.java │ │ │ │ ├── userdetails │ │ │ │ │ ├── UserDetailsView.java │ │ │ │ │ ├── LoadUserDetailsUseCase.java │ │ │ │ │ ├── UserDetailsActivity.java │ │ │ │ │ ├── UserDetailsPresenter.java │ │ │ │ │ ├── RepositoriesAdapter.java │ │ │ │ │ └── UserDetailsFragment.java │ │ │ │ ├── searchuser │ │ │ │ │ ├── SearchUserView.java │ │ │ │ │ ├── SearchUserUseCase.java │ │ │ │ │ ├── SearchUserActivity.java │ │ │ │ │ ├── SearchUserPresenter.java │ │ │ │ │ ├── UserListAdapter.java │ │ │ │ │ └── SearchUserFragment.java │ │ │ │ ├── UseCase.java │ │ │ │ └── BaseActivity.java │ │ │ │ ├── model │ │ │ │ ├── network │ │ │ │ │ ├── SearchResponse.java │ │ │ │ │ └── GitHubService.java │ │ │ │ └── entities │ │ │ │ │ ├── User.java │ │ │ │ │ └── Repository.java │ │ │ │ └── app │ │ │ │ └── MVPApplication.java │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── hugo │ │ │ └── mvpsampleapplication │ │ │ ├── utils │ │ │ ├── JobExecutorTest.java │ │ │ └── UiThreadTest.java │ │ │ ├── features │ │ │ ├── searchuser │ │ │ │ ├── UserListAdapterTest.java │ │ │ │ ├── SearchUserUseCaseTest.java │ │ │ │ └── SearchUserPresenterTest.java │ │ │ ├── UseCaseTest.java │ │ │ └── userdetails │ │ │ │ ├── LoadUserDetailsUseCaseTest.java │ │ │ │ └── UserDetailsPresenterTest.java │ │ │ └── app │ │ │ └── MVPApplicationTest.java │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── hugo │ │ │ └── mvpsampleapplication │ │ │ ├── OrientationChangeAction.java │ │ │ └── features │ │ │ ├── userdetails │ │ │ └── UserDetailsActivityTest.java │ │ │ └── searchuser │ │ │ └── SearchUserActivityTest.java │ └── sharedTest │ │ └── java │ │ └── MockFactory.java ├── proguard-rules.pro └── build.gradle ├── .idea ├── .name ├── copyright │ └── profiles_settings.xml ├── vcs.xml ├── inspectionProfiles │ ├── profiles_settings.xml │ └── Project_Default.xml ├── modules.xml ├── runConfigurations.xml ├── gradle.xml ├── compiler.xml └── misc.xml ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | MVPSampleApplication -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/octocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-mdpi/octocat.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/profile.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-mdpi/profile.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/octocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-xxhdpi/octocat.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-mdpi/placeholder.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-xxhdpi/placeholder.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-hdpi/ic_search_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-mdpi/ic_search_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-xhdpi/ic_search_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-xxhdpi/ic_search_white_36dp.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugokallstrom/MVPSampleApplication/HEAD/app/src/main/res/drawable-xxxhdpi/ic_search_white_36dp.png -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/ThreadExecutor.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils; 2 | 3 | import rx.Scheduler; 4 | 5 | public interface ThreadExecutor { 6 | Scheduler getScheduler(); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/PostExecutionThread.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils; 2 | 3 | import rx.Scheduler; 4 | 5 | public interface PostExecutionThread { 6 | Scheduler getScheduler(); 7 | } 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Oct 21 11:34:03 PDT 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/features/Presenter.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features; 2 | 3 | public interface Presenter { 4 | 5 | void attachView(V view); 6 | 7 | void detachView(); 8 | 9 | void destroy(boolean unsubscribe); 10 | } 11 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/dependencyinjection/PerActivity.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils.dependencyinjection; 2 | 3 | import java.lang.annotation.Retention; 4 | import javax.inject.Scope; 5 | 6 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 7 | 8 | @Scope 9 | @Retention(RUNTIME) 10 | public @interface PerActivity {} -------------------------------------------------------------------------------- /app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | > 2 | 3 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_search_user.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_user_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/view_progressbar.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/UiThread.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils; 2 | 3 | import javax.inject.Inject; 4 | import rx.Scheduler; 5 | import rx.android.schedulers.AndroidSchedulers; 6 | 7 | /** 8 | * Created by hugo on 2/13/16. 9 | */ 10 | public class UiThread implements PostExecutionThread { 11 | 12 | @Inject 13 | public UiThread() { 14 | 15 | } 16 | 17 | @Override 18 | public Scheduler getScheduler() { 19 | return AndroidSchedulers.mainThread(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/features/DefaultSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features; 2 | 3 | /** 4 | * Created by hugo on 1/29/16. 5 | */ 6 | public class DefaultSubscriber extends rx.Subscriber { 7 | @Override public void onCompleted() { 8 | // no-op by default. 9 | } 10 | 11 | @Override public void onError(Throwable e) { 12 | // no-op by default. 13 | } 14 | 15 | @Override public void onNext(T t) { 16 | // no-op by default. 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/test/java/com/hugo/mvpsampleapplication/utils/JobExecutorTest.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils; 2 | 3 | import org.junit.Test; 4 | import rx.schedulers.Schedulers; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * Created by hugo on 2/25/16. 10 | */ 11 | public class JobExecutorTest { 12 | 13 | @Test 14 | public void getSchedulerShouldReturnIoScheduler() { 15 | JobExecutor jobExecutor = new JobExecutor(); 16 | assertEquals(Schedulers.io(), jobExecutor.getScheduler()); 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12dp 4 | 12dp 5 | 6dp 6 | 6dp 7 | 8 | 16dp 9 | 16dp 10 | 11 | -------------------------------------------------------------------------------- /app/src/test/java/com/hugo/mvpsampleapplication/utils/UiThreadTest.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils; 2 | 3 | import org.junit.Test; 4 | import rx.android.schedulers.AndroidSchedulers; 5 | 6 | import static org.junit.Assert.assertEquals; 7 | 8 | /** 9 | * Created by hugo on 2/25/16. 10 | */ 11 | public class UiThreadTest { 12 | 13 | @Test 14 | public void getSchedulerShouldReturnAndroidSchedulersMainThread() { 15 | UiThread uiThread = new UiThread(); 16 | assertEquals(AndroidSchedulers.mainThread(), uiThread.getScheduler()); 17 | } 18 | } -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Github Browser 3 | Settings 4 | 5 | 6 | Hello blank fragment 7 | Repos 8 | Hireable 9 | Not Hireable 10 | Github Username 11 | UserDetailsActivity 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/features/userdetails/UserDetailsView.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features.userdetails; 2 | 3 | import android.content.Context; 4 | 5 | import com.hugo.mvpsampleapplication.model.entities.Repository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by hugo on 1/26/16. 11 | */ 12 | public interface UserDetailsView { 13 | 14 | void showMessage(String message); 15 | 16 | void showProgressIndicator(); 17 | 18 | void hideProgressIndicator(); 19 | 20 | void showRepositories(List repositories); 21 | 22 | Context getContext(); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/features/searchuser/SearchUserView.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features.searchuser; 2 | 3 | import android.content.Context; 4 | 5 | import com.hugo.mvpsampleapplication.model.entities.User; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by hugo on 1/22/16. 11 | */ 12 | public interface SearchUserView { 13 | 14 | void showMessage(String message); 15 | 16 | void showProgressIndicator(); 17 | 18 | void hideProgressIndicator(); 19 | 20 | void showUsers(List users); 21 | 22 | void startUserDetailsActivity(String username); 23 | 24 | Context getContext(); 25 | } 26 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /home/hugo/Android/Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_user_details.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/dependencyinjection/components/UserComponent.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils.dependencyinjection.components; 2 | 3 | import com.hugo.mvpsampleapplication.features.searchuser.SearchUserFragment; 4 | import com.hugo.mvpsampleapplication.features.userdetails.UserDetailsFragment; 5 | import com.hugo.mvpsampleapplication.utils.dependencyinjection.PerActivity; 6 | import com.hugo.mvpsampleapplication.utils.dependencyinjection.modules.UserModule; 7 | import dagger.Component; 8 | 9 | @PerActivity 10 | @Component(dependencies = ApplicationComponent.class, modules = UserModule.class) 11 | public interface UserComponent { 12 | void inject(SearchUserFragment searchUserFragment); 13 | void inject(UserDetailsFragment userDetailsFragment); 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #000000 7 | #ffffff 8 | #75ffffff 9 | #e1e1e1 10 | #3F51B5 11 | #303F9F 12 | #C5CAE9 13 | #03A9F4 14 | #212121 15 | #727272 16 | #FFFFFF 17 | #cbcbcb 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/model/network/SearchResponse.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.model.network; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | import com.hugo.mvpsampleapplication.model.entities.User; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * Created by hugo on 1/26/16. 11 | */ 12 | public class SearchResponse { 13 | @SerializedName("total_count") 14 | private int totalCount; 15 | @SerializedName("incomplete_results") 16 | private boolean incompleteResults; 17 | @SerializedName("items") 18 | private List users = new ArrayList(); 19 | 20 | public SearchResponse() {} 21 | 22 | public List getUsers() { 23 | return users; 24 | } 25 | 26 | public void setUsers(ArrayList users) { 27 | this.users = users; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/dependencyinjection/components/ApplicationComponent.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils.dependencyinjection.components; 2 | 3 | import com.hugo.mvpsampleapplication.features.BaseActivity; 4 | import com.hugo.mvpsampleapplication.model.network.GitHubService; 5 | import com.hugo.mvpsampleapplication.utils.PostExecutionThread; 6 | import com.hugo.mvpsampleapplication.utils.ThreadExecutor; 7 | import com.hugo.mvpsampleapplication.utils.dependencyinjection.modules.ApplicationModule; 8 | import dagger.Component; 9 | import javax.inject.Singleton; 10 | 11 | @Singleton 12 | @Component(modules = ApplicationModule.class) 13 | public interface ApplicationComponent { 14 | void inject(BaseActivity baseActivity); 15 | 16 | GitHubService gitHubService(); 17 | ThreadExecutor threadExecutor(); 18 | PostExecutionThread postExecutionThread(); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/features/searchuser/SearchUserUseCase.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features.searchuser; 2 | 3 | import com.hugo.mvpsampleapplication.features.UseCase; 4 | import com.hugo.mvpsampleapplication.model.network.GitHubService; 5 | 6 | import com.hugo.mvpsampleapplication.utils.PostExecutionThread; 7 | import com.hugo.mvpsampleapplication.utils.ThreadExecutor; 8 | import javax.inject.Inject; 9 | import rx.Observable; 10 | 11 | public class SearchUserUseCase extends UseCase { 12 | 13 | @Inject 14 | public SearchUserUseCase(GitHubService gitHubService, ThreadExecutor threadExecutor, 15 | PostExecutionThread postExecutionThread) { 16 | super(gitHubService, threadExecutor, postExecutionThread); 17 | } 18 | 19 | @Override 20 | public Observable buildUseCase(String username) throws NullPointerException { 21 | if (username == null) { 22 | throw new NullPointerException("Query must not be null"); 23 | } 24 | return getGitHubService().searchUser(username); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/features/userdetails/LoadUserDetailsUseCase.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features.userdetails; 2 | 3 | import com.hugo.mvpsampleapplication.features.UseCase; 4 | import com.hugo.mvpsampleapplication.model.network.GitHubService; 5 | 6 | import com.hugo.mvpsampleapplication.utils.PostExecutionThread; 7 | import com.hugo.mvpsampleapplication.utils.ThreadExecutor; 8 | import javax.inject.Inject; 9 | import rx.Observable; 10 | 11 | public class LoadUserDetailsUseCase extends UseCase { 12 | 13 | 14 | @Inject 15 | public LoadUserDetailsUseCase(GitHubService gitHubService, ThreadExecutor threadExecutor, 16 | PostExecutionThread postExecutionThread) { 17 | super(gitHubService, threadExecutor, postExecutionThread); 18 | } 19 | 20 | @Override public Observable buildUseCase(String username) throws NullPointerException { 21 | if (username == null) { 22 | throw new NullPointerException("Username must not be null"); 23 | } 24 | return getGitHubService().getRepositoriesFromUser(username); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/test/java/com/hugo/mvpsampleapplication/features/searchuser/UserListAdapterTest.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features.searchuser; 2 | 3 | import com.hugo.mvpsampleapplication.MockFactory; 4 | import com.hugo.mvpsampleapplication.model.entities.User; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.mockito.runners.MockitoJUnitRunner; 11 | 12 | import static org.junit.Assert.*; 13 | 14 | @RunWith(MockitoJUnitRunner.class) 15 | public class UserListAdapterTest { 16 | 17 | private List users = new ArrayList<>(); 18 | private UserListAdapter userListAdapter; 19 | 20 | @Before 21 | public void setUp() { 22 | User user = MockFactory.buildMockUser(); 23 | users.add(user); 24 | userListAdapter = new UserListAdapter(); 25 | } 26 | 27 | @Test 28 | public void getItemCountShouldReturnNumberOfUsersInList() { 29 | userListAdapter.setUsers(users); 30 | int itemCount = userListAdapter.getItemCount(); 31 | assertEquals(users.size(), itemCount); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/JobExecutor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2015 Fernando Cejas Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.hugo.mvpsampleapplication.utils; 17 | 18 | import javax.inject.Inject; 19 | import rx.Scheduler; 20 | import rx.schedulers.Schedulers; 21 | 22 | public class JobExecutor implements ThreadExecutor { 23 | 24 | @Inject 25 | public JobExecutor() { 26 | 27 | } 28 | 29 | @Override 30 | public Scheduler getScheduler() { 31 | return Schedulers.io(); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/res/layout-large/activity_search_user.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 17 | 18 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/features/userdetails/UserDetailsActivity.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features.userdetails; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import com.hugo.mvpsampleapplication.R; 8 | import com.hugo.mvpsampleapplication.features.BaseActivity; 9 | 10 | public class UserDetailsActivity extends BaseActivity { 11 | 12 | private static final String EXTRA_USERNAME = "USERNAME"; 13 | 14 | public static Intent newIntent(Context context, String username) { 15 | Intent intent = new Intent(context, UserDetailsActivity.class); 16 | intent.putExtra(EXTRA_USERNAME, username); 17 | return intent; 18 | } 19 | 20 | @Override protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_user_details); 23 | String username = getIntent().getStringExtra(EXTRA_USERNAME); 24 | if (getFragmentManager().findFragmentById(R.id.content_activity_user_details) == null) { 25 | addFragment(R.id.content_activity_user_details, UserDetailsFragment.newInstance(username)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/test/java/com/hugo/mvpsampleapplication/app/MVPApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.app; 2 | 3 | import com.hugo.mvpsampleapplication.utils.dependencyinjection.components.ApplicationComponent; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | import org.mockito.Mock; 7 | import org.mockito.MockitoAnnotations; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | /** 12 | * Created by hugo on 2/25/16. 13 | */ 14 | public class MVPApplicationTest { 15 | 16 | @Mock 17 | private ApplicationComponent applicationComponent; 18 | private MVPApplication mvpApplication; 19 | 20 | @Before 21 | public void setUp() { 22 | MockitoAnnotations.initMocks(this); 23 | mvpApplication = new MVPApplication(); 24 | } 25 | 26 | @Test 27 | public void testSetAndGetApplicationComponent() throws Exception { 28 | ApplicationComponent nullApplicationComponent = mvpApplication.getApplicationComponent(); 29 | assertNull(nullApplicationComponent); 30 | 31 | mvpApplication.setApplicationComponent(applicationComponent); 32 | applicationComponent = mvpApplication.getApplicationComponent(); 33 | assertNotNull(applicationComponent); 34 | } 35 | 36 | 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/model/entities/User.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.model.entities; 2 | 3 | import com.google.gson.annotations.SerializedName; 4 | 5 | /** 6 | * Created by hugo on 1/22/16. 7 | */ 8 | public class User { 9 | 10 | private long id; 11 | private String login; 12 | @SerializedName("avatar_url") 13 | private String avatarUrl; 14 | @SerializedName("repos_url") 15 | private String reposUrl; 16 | 17 | public User() {} 18 | 19 | public long getId() { 20 | return id; 21 | } 22 | 23 | public void setId(int id) { 24 | this.id = id; 25 | } 26 | 27 | public String getLogin() { 28 | return login; 29 | } 30 | 31 | public void setLogin(String login) { 32 | this.login = login; 33 | } 34 | 35 | public String getAvatarUrl() { 36 | return avatarUrl; 37 | } 38 | 39 | public void setAvatarUrl(String avatarUrl) { 40 | this.avatarUrl = avatarUrl; 41 | } 42 | 43 | public String getReposUrl() { 44 | return reposUrl; 45 | } 46 | 47 | public void setReposUrl(String reposUrl) { 48 | this.reposUrl = reposUrl; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/app/MVPApplication.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.app; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import com.hugo.mvpsampleapplication.model.network.GitHubService; 7 | 8 | import com.hugo.mvpsampleapplication.utils.dependencyinjection.components.ApplicationComponent; 9 | import com.hugo.mvpsampleapplication.utils.dependencyinjection.components.DaggerApplicationComponent; 10 | import com.hugo.mvpsampleapplication.utils.dependencyinjection.modules.ApplicationModule; 11 | import rx.Scheduler; 12 | import rx.schedulers.Schedulers; 13 | 14 | public class MVPApplication extends Application { 15 | 16 | private ApplicationComponent applicationComponent; 17 | 18 | @Override 19 | public void onCreate() { 20 | super.onCreate(); 21 | if(applicationComponent == null) { 22 | applicationComponent = 23 | DaggerApplicationComponent.builder().applicationModule(new ApplicationModule()).build(); 24 | } 25 | } 26 | 27 | public void setApplicationComponent(ApplicationComponent applicationComponent) { 28 | this.applicationComponent = applicationComponent; 29 | } 30 | 31 | public ApplicationComponent getApplicationComponent() { 32 | return applicationComponent; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/model/network/GitHubService.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.model.network; 2 | 3 | import com.hugo.mvpsampleapplication.model.entities.Repository; 4 | 5 | import java.util.List; 6 | 7 | import retrofit.GsonConverterFactory; 8 | import retrofit.Retrofit; 9 | import retrofit.RxJavaCallAdapterFactory; 10 | import retrofit.http.GET; 11 | import retrofit.http.Path; 12 | import retrofit.http.Query; 13 | import rx.Observable; 14 | 15 | public interface GitHubService { 16 | 17 | String endpoint = "https://api.github.com/"; 18 | String getRepoUrl = "users/{username}/repos"; 19 | String searchUserUrl = "search/users"; 20 | 21 | @GET(getRepoUrl) 22 | Observable> getRepositoriesFromUser(@Path("username") String username); 23 | 24 | @GET(searchUserUrl) 25 | Observable searchUser(@Query("q") String username); 26 | 27 | class Factory { 28 | public static GitHubService create() { 29 | Retrofit retrofit = new Retrofit.Builder() 30 | .baseUrl(endpoint) 31 | .addConverterFactory(GsonConverterFactory.create()) 32 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 33 | .build(); 34 | return retrofit.create(GitHubService.class); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/features/searchuser/SearchUserActivity.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.features.searchuser; 2 | 3 | import android.os.Bundle; 4 | 5 | import android.util.Log; 6 | import com.hugo.mvpsampleapplication.R; 7 | import com.hugo.mvpsampleapplication.features.BaseActivity; 8 | import com.hugo.mvpsampleapplication.features.userdetails.UserDetailsActivity; 9 | import com.hugo.mvpsampleapplication.features.userdetails.UserDetailsFragment; 10 | 11 | public class SearchUserActivity extends BaseActivity 12 | implements SearchUserFragment.ActivityListener { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_search_user); 18 | if (savedInstanceState == null) { 19 | addFragment(R.id.content_activity_search_user, SearchUserFragment.newInstance()); 20 | } 21 | } 22 | 23 | @Override 24 | public void startUserDetails(String username) { 25 | UserDetailsFragment userDetailsFragment = 26 | (UserDetailsFragment) getSupportFragmentManager().findFragmentById( 27 | R.id.userDetailsFragment); 28 | if (userDetailsFragment == null) { 29 | Log.d("activity", "starting user details " + username); 30 | startActivity(UserDetailsActivity.newIntent(this, username)); 31 | } else { 32 | userDetailsFragment.loadRepositories(username); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/dependencyinjection/modules/UserModule.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils.dependencyinjection.modules; 2 | 3 | 4 | import com.hugo.mvpsampleapplication.features.UseCase; 5 | import com.hugo.mvpsampleapplication.features.searchuser.SearchUserUseCase; 6 | import com.hugo.mvpsampleapplication.features.userdetails.LoadUserDetailsUseCase; 7 | import com.hugo.mvpsampleapplication.model.network.GitHubService; 8 | import com.hugo.mvpsampleapplication.utils.PostExecutionThread; 9 | import com.hugo.mvpsampleapplication.utils.ThreadExecutor; 10 | import com.hugo.mvpsampleapplication.utils.dependencyinjection.PerActivity; 11 | import dagger.Module; 12 | import dagger.Provides; 13 | import javax.inject.Named; 14 | 15 | @Module 16 | public class UserModule { 17 | 18 | public UserModule() { 19 | } 20 | 21 | @Provides 22 | @PerActivity 23 | @Named("searchUser") 24 | public UseCase provideSearchUserUseCase(GitHubService gitHubService, ThreadExecutor threadExecutor, 25 | PostExecutionThread postExecutionThread) { 26 | return new SearchUserUseCase(gitHubService, threadExecutor, postExecutionThread); 27 | } 28 | 29 | @Provides 30 | @PerActivity 31 | @Named("userDetails") 32 | public UseCase provideUserDetailsUseCase(GitHubService gitHubService, ThreadExecutor threadExecutor, 33 | PostExecutionThread postExecutionThread) { 34 | return new LoadUserDetailsUseCase(gitHubService, threadExecutor, postExecutionThread); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/hugo/mvpsampleapplication/utils/dependencyinjection/modules/ApplicationModule.java: -------------------------------------------------------------------------------- 1 | package com.hugo.mvpsampleapplication.utils.dependencyinjection.modules; 2 | 3 | import com.hugo.mvpsampleapplication.model.network.GitHubService; 4 | import com.hugo.mvpsampleapplication.utils.JobExecutor; 5 | import com.hugo.mvpsampleapplication.utils.PostExecutionThread; 6 | import com.hugo.mvpsampleapplication.utils.ThreadExecutor; 7 | import com.hugo.mvpsampleapplication.utils.UiThread; 8 | import dagger.Module; 9 | import dagger.Provides; 10 | import javax.inject.Singleton; 11 | import retrofit.GsonConverterFactory; 12 | import retrofit.Retrofit; 13 | import retrofit.RxJavaCallAdapterFactory; 14 | 15 | @Module 16 | public class ApplicationModule { 17 | 18 | public ApplicationModule() { 19 | 20 | } 21 | 22 | @Provides 23 | @Singleton 24 | public GitHubService provideGitHubService() { 25 | Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.github.com/") 26 | .addConverterFactory(GsonConverterFactory.create()) 27 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 28 | .build(); 29 | return retrofit.create(GitHubService.class); 30 | } 31 | 32 | @Provides 33 | @Singleton 34 | public ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) { 35 | return jobExecutor; 36 | } 37 | 38 | @Provides 39 | @Singleton 40 | public PostExecutionThread providePostExecutionThread(UiThread uiThread) { 41 | return uiThread; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 15 | 16 | 23 | 24 | 31 | 32 |