├── VERSION ├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-hdpi │ │ │ │ ├── ic_like_normal.png │ │ │ │ └── ic_like_selected.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── ic_like_normal.png │ │ │ │ └── ic_like_selected.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-xhdpi │ │ │ │ ├── ic_like_normal.png │ │ │ │ ├── ic_like_disabled.png │ │ │ │ └── ic_like_selected.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── ic_like_normal.png │ │ │ │ └── ic_like_selected.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── ic_like_normal.png │ │ │ │ └── ic_like_selected.png │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── drawable │ │ │ │ ├── item_repository_backgound.xml │ │ │ │ └── ckeck_box_like_background.xml │ │ │ ├── menu │ │ │ │ └── menu_home.xml │ │ │ ├── values-w820dp │ │ │ │ └── dimens.xml │ │ │ └── layout │ │ │ │ ├── fragment_repository_details.xml │ │ │ │ ├── item_repository.xml │ │ │ │ ├── activity_home.xml │ │ │ │ └── activity_sign_in.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── arellomobile │ │ │ │ └── github │ │ │ │ ├── mvp │ │ │ │ ├── views │ │ │ │ │ ├── SignOutView.java │ │ │ │ │ ├── SplashView.java │ │ │ │ │ ├── HomeView.java │ │ │ │ │ ├── RepositoryView.java │ │ │ │ │ ├── RepositoryLikesView.java │ │ │ │ │ ├── SignInView.java │ │ │ │ │ └── RepositoriesView.java │ │ │ │ ├── common │ │ │ │ │ ├── AuthUtils.java │ │ │ │ │ ├── PrefUtils.java │ │ │ │ │ └── RxUtils.java │ │ │ │ ├── models │ │ │ │ │ ├── gson │ │ │ │ │ │ └── SearchResult.java │ │ │ │ │ ├── User.java │ │ │ │ │ └── Repository.java │ │ │ │ ├── presenters │ │ │ │ │ ├── SignOutPresenter.java │ │ │ │ │ ├── HomePresenter.java │ │ │ │ │ ├── SplashPresenter.java │ │ │ │ │ ├── RepositoryPresenter.java │ │ │ │ │ ├── SignInPresenter.java │ │ │ │ │ ├── RepositoryLikesPresenter.java │ │ │ │ │ └── RepositoriesPresenter.java │ │ │ │ └── GithubService.java │ │ │ │ ├── di │ │ │ │ ├── modules │ │ │ │ │ ├── BusModule.java │ │ │ │ │ ├── ApiModule.java │ │ │ │ │ ├── ContextModule.java │ │ │ │ │ ├── GithubModule.java │ │ │ │ │ └── RetrofitModule.java │ │ │ │ └── AppComponent.java │ │ │ │ ├── app │ │ │ │ ├── GithubError.java │ │ │ │ ├── GithubApp.java │ │ │ │ └── GithubApi.java │ │ │ │ └── ui │ │ │ │ ├── adapters │ │ │ │ ├── MvpBaseAdapter.java │ │ │ │ └── RepositoriesAdapter.java │ │ │ │ ├── views │ │ │ │ ├── FrameSwipeRefreshLayout.java │ │ │ │ └── LikeButton.java │ │ │ │ ├── activities │ │ │ │ ├── SplashActivity.java │ │ │ │ ├── SignInActivity.java │ │ │ │ └── HomeActivity.java │ │ │ │ └── fragments │ │ │ │ └── DetailsFragment.java │ │ └── AndroidManifest.xml │ └── androidTest │ │ └── java │ │ └── com │ │ └── arellomobile │ │ └── github │ │ └── ApplicationTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── LICENSE ├── README.md ├── gradlew.bat └── gradlew /VERSION: -------------------------------------------------------------------------------- 1 | 0.3.1 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_like_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-hdpi/ic_like_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_like_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-mdpi/ic_like_normal.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_like_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-hdpi/ic_like_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_like_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-mdpi/ic_like_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_like_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-xhdpi/ic_like_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_like_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-xxhdpi/ic_like_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_like_disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-xhdpi/ic_like_disabled.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_like_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-xhdpi/ic_like_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_like_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-xxhdpi/ic_like_selected.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_like_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-xxxhdpi/ic_like_normal.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_like_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arello-Mobile/MoxySample/HEAD/app/src/main/res/drawable-xxxhdpi/ic_like_selected.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | */local.properties 4 | local.properties 5 | .DS_Store 6 | */captures 7 | build/ 8 | .idea/ 9 | moxy/mvpapi/artifacts/ 10 | moxy/mvpcompiler/artifacts/ 11 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Sep 20 20:08:10 KRAT 2016 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.14.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/item_repository_backgound.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/menu/menu_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/views/SignOutView.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.views; 2 | 3 | import com.arellomobile.mvp.MvpView; 4 | 5 | /** 6 | * Date: 18.01.2016 7 | * Time: 16:04 8 | * 9 | * @author Yuri Shmakov 10 | */ 11 | public interface SignOutView extends MvpView { 12 | void signOut(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/views/SplashView.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.views; 2 | 3 | import com.arellomobile.mvp.MvpView; 4 | 5 | /** 6 | * Date: 18.01.2016 7 | * Time: 15:39 8 | * 9 | * @author Yuri Shmakov 10 | */ 11 | public interface SplashView extends MvpView { 12 | void setAuthorized(boolean isAuthorized); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ckeck_box_like_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/views/HomeView.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.views; 2 | 3 | import com.arellomobile.github.mvp.models.Repository; 4 | import com.arellomobile.mvp.MvpView; 5 | 6 | /** 7 | * Date: 27.01.2016 8 | * Time: 20:00 9 | * 10 | * @author Yuri Shmakov 11 | */ 12 | public interface HomeView extends MvpView { 13 | void showDetails(int position, Repository repository); 14 | } 15 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/arellomobile/github/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase 10 | { 11 | public ApplicationTest() 12 | { 13 | super(Application.class); 14 | } 15 | } -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/views/RepositoryView.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.views; 2 | 3 | import com.arellomobile.github.mvp.models.Repository; 4 | import com.arellomobile.mvp.MvpView; 5 | 6 | /** 7 | * Date: 27.01.2016 8 | * Time: 21:13 9 | * 10 | * @author Yuri Shmakov 11 | */ 12 | public interface RepositoryView extends MvpView { 13 | void showRepository(Repository repository); 14 | 15 | void updateLike(boolean isInProgress, boolean isLiked); 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/common/AuthUtils.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.common; 2 | 3 | /** 4 | * Date: 18.01.2016 5 | * Time: 15:02 6 | * 7 | * @author Yuri Shmakov 8 | */ 9 | public class AuthUtils { 10 | private static final String TOKEN = "token"; 11 | 12 | public static String getToken() { 13 | return PrefUtils.getPrefs().getString(TOKEN, ""); 14 | } 15 | 16 | public static void setToken(String token) { 17 | PrefUtils.getEditor().putString(TOKEN, token).commit(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/models/gson/SearchResult.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.models.gson; 2 | 3 | import java.util.List; 4 | 5 | import com.arellomobile.github.mvp.models.Repository; 6 | 7 | /** 8 | * Date: 18.01.2016 9 | * Time: 12:13 10 | * 11 | * @author Yuri Shmakov 12 | */ 13 | public class SearchResult { 14 | private int mTotalCount; 15 | private boolean mIncompleteResults; 16 | private List mItems; 17 | 18 | public List getRepositories() { 19 | return mItems; 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/di/modules/BusModule.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.di.modules; 2 | 3 | import com.arellomobile.github.app.GithubApi; 4 | import com.squareup.otto.Bus; 5 | 6 | import javax.inject.Singleton; 7 | 8 | import dagger.Module; 9 | import dagger.Provides; 10 | 11 | /** 12 | * Date: 20.09.2016 13 | * Time: 20:22 14 | * 15 | * @author Yuri Shmakov 16 | */ 17 | @Module 18 | public class BusModule { 19 | @Provides 20 | @Singleton 21 | public Bus provideBus(GithubApi authApi) { 22 | return new Bus(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/di/modules/ApiModule.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.di.modules; 2 | 3 | import com.arellomobile.github.app.GithubApi; 4 | 5 | import javax.inject.Singleton; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | import retrofit2.Retrofit; 10 | 11 | /** 12 | * Date: 9/2/2016 13 | * Time: 18:54 14 | * 15 | * @author Artur Artikov 16 | */ 17 | @Module(includes = {RetrofitModule.class}) 18 | public class ApiModule { 19 | @Provides 20 | @Singleton 21 | public GithubApi provideAuthApi(Retrofit retrofit) { 22 | return retrofit.create(GithubApi.class); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/di/modules/ContextModule.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.di.modules; 2 | 3 | import android.content.Context; 4 | 5 | import javax.inject.Singleton; 6 | 7 | import dagger.Module; 8 | import dagger.Provides; 9 | 10 | /** 11 | * Date: 8/18/2016 12 | * Time: 14:50 13 | * 14 | * @author Artur Artikov 15 | */ 16 | @Module 17 | public class ContextModule { 18 | private Context mContext; 19 | 20 | public ContextModule(Context context) { 21 | mContext = context; 22 | } 23 | 24 | @Provides 25 | @Singleton 26 | public Context provideContext() { 27 | return mContext; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/views/RepositoryLikesView.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.views; 2 | 3 | import java.util.List; 4 | 5 | import com.arellomobile.mvp.MvpView; 6 | import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy; 7 | import com.arellomobile.mvp.viewstate.strategy.StateStrategyType; 8 | 9 | /** 10 | * Date: 26.01.2016 11 | * Time: 16:33 12 | * 13 | * @author Yuri Shmakov 14 | */ 15 | public interface RepositoryLikesView extends MvpView { 16 | @StateStrategyType(AddToEndSingleStrategy.class) 17 | void updateLikes(List inProgress, List likedIds); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/presenters/SignOutPresenter.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.presenters; 2 | 3 | import com.arellomobile.github.mvp.common.AuthUtils; 4 | import com.arellomobile.github.mvp.views.SignOutView; 5 | import com.arellomobile.mvp.InjectViewState; 6 | import com.arellomobile.mvp.MvpPresenter; 7 | 8 | /** 9 | * Date: 18.01.2016 10 | * Time: 16:03 11 | * 12 | * @author Yuri Shmakov 13 | */ 14 | @InjectViewState 15 | public class SignOutPresenter extends MvpPresenter { 16 | public void signOut() { 17 | AuthUtils.setToken(""); 18 | 19 | getViewState().signOut(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/di/modules/GithubModule.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.di.modules; 2 | 3 | 4 | import com.arellomobile.github.app.GithubApi; 5 | import com.arellomobile.github.mvp.GithubService; 6 | 7 | import javax.inject.Singleton; 8 | 9 | import dagger.Module; 10 | import dagger.Provides; 11 | 12 | /** 13 | * Date: 8/26/2016 14 | * Time: 11:58 15 | * 16 | * @author Artur Artikov 17 | */ 18 | 19 | @Module(includes = {ApiModule.class}) 20 | public class GithubModule { 21 | @Provides 22 | @Singleton 23 | public GithubService provideGithubService(GithubApi authApi) { 24 | return new GithubService(authApi); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/presenters/HomePresenter.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.presenters; 2 | 3 | import com.arellomobile.github.mvp.models.Repository; 4 | import com.arellomobile.github.mvp.views.HomeView; 5 | import com.arellomobile.mvp.InjectViewState; 6 | import com.arellomobile.mvp.MvpPresenter; 7 | 8 | /** 9 | * Date: 27.01.2016 10 | * Time: 19:59 11 | * 12 | * @author Yuri Shmakov 13 | */ 14 | @InjectViewState 15 | public class HomePresenter extends MvpPresenter { 16 | public void onRepositorySelection(int position, Repository repository) { 17 | getViewState().showDetails(position, repository); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/common/PrefUtils.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.common; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import com.arellomobile.github.app.GithubApp; 7 | 8 | /** 9 | * Date: 18.01.2016 10 | * Time: 15:01 11 | * 12 | * @author Yuri Shmakov 13 | */ 14 | public class PrefUtils { 15 | private static final String PREF_NAME = "github"; 16 | 17 | public static SharedPreferences getPrefs() { 18 | return GithubApp.getAppComponent().getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); 19 | } 20 | 21 | public static SharedPreferences.Editor getEditor() { 22 | return getPrefs().edit(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /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 C:\Programs\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/java/com/arellomobile/github/app/GithubError.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.app; 2 | 3 | import java.io.IOException; 4 | 5 | import org.json.JSONException; 6 | import org.json.JSONObject; 7 | 8 | import okhttp3.ResponseBody; 9 | 10 | /** 11 | * Date: 18.01.2016 12 | * Time: 14:11 13 | * 14 | * @author Yuri Shmakov 15 | */ 16 | public class GithubError extends Throwable { 17 | public GithubError(ResponseBody responseBody) { 18 | super(getMessage(responseBody)); 19 | } 20 | 21 | private static String getMessage(ResponseBody responseBody) { 22 | try { 23 | return new JSONObject(responseBody.string()).optString("message"); 24 | } catch (JSONException | IOException e) { 25 | e.printStackTrace(); 26 | } 27 | 28 | return "Unknown exception"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Github 3 | 4 | 5 | Email 6 | Password (optional) 7 | Sign in or register 8 | Sign in 9 | This email address is invalid 10 | This password is too short 11 | This password is incorrect 12 | This field is required 13 | "Contacts permissions are needed for providing email completions." 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/app/GithubApp.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.app; 2 | 3 | import android.app.Application; 4 | 5 | import com.arellomobile.github.di.AppComponent; 6 | import com.arellomobile.github.di.DaggerAppComponent; 7 | import com.arellomobile.github.di.modules.ContextModule; 8 | 9 | /** 10 | * Date: 18.01.2016 11 | * Time: 11:22 12 | * 13 | * @author Yuri Shmakov 14 | */ 15 | public class GithubApp extends Application { 16 | private static AppComponent sAppComponent; 17 | 18 | @Override 19 | public void onCreate() { 20 | super.onCreate(); 21 | 22 | sAppComponent = DaggerAppComponent.builder() 23 | .contextModule(new ContextModule(this)) 24 | .build(); 25 | 26 | } 27 | 28 | public static AppComponent getAppComponent() { 29 | return sAppComponent; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/views/SignInView.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.views; 2 | 3 | import com.arellomobile.mvp.MvpView; 4 | import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy; 5 | import com.arellomobile.mvp.viewstate.strategy.SkipStrategy; 6 | import com.arellomobile.mvp.viewstate.strategy.StateStrategyType; 7 | 8 | /** 9 | * Date: 15.01.2016 10 | * Time: 18:41 11 | * 12 | * @author Yuri Shmakov 13 | */ 14 | @StateStrategyType(AddToEndSingleStrategy.class) 15 | public interface SignInView extends MvpView { 16 | void showProgress(); 17 | 18 | void hideProgress(); 19 | 20 | void showError(String message); 21 | 22 | void hideError(); 23 | 24 | void showError(Integer emailError, Integer passwordError); 25 | 26 | @StateStrategyType(SkipStrategy.class) 27 | void successSignIn(); 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/GithubService.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp; 2 | 3 | import java.util.List; 4 | 5 | import com.arellomobile.github.app.GithubApi; 6 | import com.arellomobile.github.mvp.models.Repository; 7 | import com.arellomobile.github.mvp.models.User; 8 | 9 | import retrofit2.Call; 10 | 11 | /** 12 | * Date: 20.09.2016 13 | * Time: 20:14 14 | * 15 | * @author Yuri Shmakov 16 | */ 17 | 18 | public class GithubService { 19 | 20 | private GithubApi mGithubApi; 21 | 22 | public GithubService(GithubApi githubApi) { 23 | mGithubApi = githubApi; 24 | } 25 | 26 | 27 | public Call signIn(String token) { 28 | return mGithubApi.signIn(token); 29 | } 30 | 31 | public Call> getUserRepos(String user, int page, Integer pageSize) { 32 | return mGithubApi.getUserRepos(user, page, pageSize); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/presenters/SplashPresenter.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.presenters; 2 | 3 | import android.text.TextUtils; 4 | 5 | import com.arellomobile.github.mvp.common.AuthUtils; 6 | import com.arellomobile.github.mvp.views.SplashView; 7 | import com.arellomobile.mvp.MvpPresenter; 8 | 9 | import rx.Observable; 10 | 11 | /** 12 | * Date: 18.01.2016 13 | * Time: 15:38 14 | * 15 | * @author Yuri Shmakov 16 | */ 17 | public class SplashPresenter extends MvpPresenter { 18 | public void checkAuthorized() { 19 | final Observable getTokenObservable = Observable.create(subscriber -> subscriber.onNext(AuthUtils.getToken())); 20 | 21 | getTokenObservable.subscribe(token -> { 22 | for (SplashView splashView : getAttachedViews()) { 23 | splashView.setAuthorized(!TextUtils.isEmpty(token)); 24 | } 25 | }); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_repository_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 13 | 14 | 21 | 22 | -------------------------------------------------------------------------------- /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/arellomobile/github/ui/adapters/MvpBaseAdapter.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.ui.adapters; 2 | 3 | import android.widget.BaseAdapter; 4 | 5 | import com.arellomobile.mvp.MvpDelegate; 6 | 7 | /** 8 | * Date: 26.01.2016 9 | * Time: 17:26 10 | * 11 | * @author Yuri Shmakov 12 | */ 13 | public abstract class MvpBaseAdapter extends BaseAdapter { 14 | private MvpDelegate mMvpDelegate; 15 | private MvpDelegate mParentDelegate; 16 | private String mChildId; 17 | 18 | public MvpBaseAdapter(MvpDelegate parentDelegate, String childId) { 19 | mParentDelegate = parentDelegate; 20 | mChildId = childId; 21 | 22 | getMvpDelegate().onCreate(); 23 | } 24 | 25 | public MvpDelegate getMvpDelegate() { 26 | if (mMvpDelegate == null) { 27 | mMvpDelegate = new MvpDelegate<>(this); 28 | mMvpDelegate.setParentDelegate(mParentDelegate, mChildId); 29 | 30 | } 31 | return mMvpDelegate; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/app/GithubApi.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.app; 2 | 3 | import java.util.List; 4 | 5 | import com.arellomobile.github.mvp.models.Repository; 6 | import com.arellomobile.github.mvp.models.User; 7 | import com.arellomobile.github.mvp.models.gson.SearchResult; 8 | 9 | import retrofit2.Call; 10 | import retrofit2.http.GET; 11 | import retrofit2.http.Header; 12 | import retrofit2.http.Path; 13 | import retrofit2.http.Query; 14 | 15 | /** 16 | * Date: 18.01.2016 17 | * Time: 12:07 18 | * 19 | * @author Yuri Shmakov 20 | */ 21 | public interface GithubApi { 22 | Integer PAGE_SIZE = 50; 23 | 24 | @GET("/user") 25 | Call signIn(@Header("Authorization") String token); 26 | 27 | @GET("/search/repositories?sort=stars&order=desc") 28 | Call search(@Query("q") String query); 29 | 30 | @GET("/users/{login}/repos") 31 | Call> getUserRepos(@Path("login") String login, @Query("page") int page, @Query("per_page") int pageSize); 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_repository.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/ui/views/FrameSwipeRefreshLayout.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.ui.views; 2 | 3 | import android.content.Context; 4 | import android.support.v4.widget.SwipeRefreshLayout; 5 | import android.util.AttributeSet; 6 | import android.widget.ListView; 7 | 8 | /** 9 | * Date: 26.01.2016 10 | * Time: 14:54 11 | * 12 | * @author Yuri Shmakov 13 | */ 14 | public class FrameSwipeRefreshLayout extends SwipeRefreshLayout { 15 | private ListView mListViewChild; 16 | 17 | public FrameSwipeRefreshLayout(Context context) { 18 | super(context); 19 | } 20 | 21 | public FrameSwipeRefreshLayout(Context context, AttributeSet attrs) { 22 | super(context, attrs); 23 | } 24 | 25 | public void setListViewChild(ListView listViewChild) { 26 | mListViewChild = listViewChild; 27 | } 28 | 29 | @Override 30 | public boolean canChildScrollUp() { 31 | if (mListViewChild != null && mListViewChild.getVisibility() == VISIBLE) { 32 | return mListViewChild.canScrollVertically(-1); 33 | } 34 | 35 | return super.canChildScrollUp(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Arello Mobile 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/views/RepositoriesView.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.views; 2 | 3 | import java.util.List; 4 | 5 | import com.arellomobile.github.mvp.models.Repository; 6 | import com.arellomobile.mvp.MvpView; 7 | import com.arellomobile.mvp.viewstate.strategy.AddToEndSingleStrategy; 8 | import com.arellomobile.mvp.viewstate.strategy.AddToEndStrategy; 9 | import com.arellomobile.mvp.viewstate.strategy.StateStrategyType; 10 | 11 | /** 12 | * Date: 22.01.2016 13 | * Time: 14:40 14 | * 15 | * @author Yuri Shmakov 16 | */ 17 | @StateStrategyType(AddToEndSingleStrategy.class) 18 | public interface RepositoriesView extends MvpView { 19 | void showError(String message); 20 | 21 | void hideError(); 22 | 23 | void onStartLoading(); 24 | 25 | void onFinishLoading(); 26 | 27 | void showRefreshing(); 28 | 29 | void hideRefreshing(); 30 | 31 | void showListProgress(); 32 | 33 | void hideListProgress(); 34 | 35 | void setRepositories(List repositories, boolean maybeMore); 36 | 37 | @StateStrategyType(AddToEndStrategy.class) 38 | void addRepositories(List repositories, boolean maybeMore); 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/di/AppComponent.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.di; 2 | 3 | import android.content.Context; 4 | 5 | import com.arellomobile.github.di.modules.BusModule; 6 | import com.arellomobile.github.di.modules.ContextModule; 7 | import com.arellomobile.github.di.modules.GithubModule; 8 | import com.arellomobile.github.mvp.GithubService; 9 | import com.arellomobile.github.mvp.presenters.RepositoriesPresenter; 10 | import com.arellomobile.github.mvp.presenters.RepositoryLikesPresenter; 11 | import com.arellomobile.github.mvp.presenters.SignInPresenter; 12 | import com.squareup.otto.Bus; 13 | 14 | import javax.inject.Singleton; 15 | 16 | import dagger.Component; 17 | 18 | /** 19 | * Date: 8/18/2016 20 | * Time: 14:49 21 | * 22 | * @author Artur Artikov 23 | */ 24 | @Singleton 25 | @Component(modules = {ContextModule.class, BusModule.class, GithubModule.class}) 26 | public interface AppComponent { 27 | Context getContext(); 28 | GithubService getAuthService(); 29 | Bus getBus(); 30 | 31 | void inject(SignInPresenter presenter); 32 | void inject(RepositoriesPresenter repositoriesPresenter); 33 | void inject(RepositoryLikesPresenter repositoryLikesPresenter); 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/ui/activities/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.ui.activities; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | 6 | import com.arellomobile.github.mvp.presenters.SplashPresenter; 7 | import com.arellomobile.github.mvp.views.SplashView; 8 | import com.arellomobile.mvp.MvpAppCompatActivity; 9 | import com.arellomobile.mvp.presenter.InjectPresenter; 10 | 11 | public class SplashActivity extends MvpAppCompatActivity implements SplashView { 12 | @InjectPresenter 13 | SplashPresenter mSplashPresenter; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | 19 | // By default view attaches to presenter in onStart() method, 20 | // but we attach it manually for earlier check authorization state. 21 | getMvpDelegate().onAttach(); 22 | 23 | mSplashPresenter.checkAuthorized(); 24 | } 25 | 26 | @Override 27 | public void setAuthorized(boolean isAuthorized) { 28 | startActivityForResult(new Intent(this, isAuthorized ? HomeActivity.class : SignInActivity.class), 0); 29 | } 30 | 31 | @Override 32 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 33 | finish(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Deprecated! 2 | Sample are located in base [Moxy repository](https://github.com/Arello-Mobile/Moxy/tree/master/sample-github) 3 | 4 | # License 5 | ``` 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2016 Arello Mobile 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | ``` 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/ui/views/LikeButton.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.ui.views; 2 | 3 | import android.annotation.TargetApi; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.util.AttributeSet; 7 | import android.widget.Button; 8 | import android.widget.Checkable; 9 | 10 | /** 11 | * Date: 26.01.2016 12 | * Time: 17:19 13 | * 14 | * @author Yuri Shmakov 15 | */ 16 | public class LikeButton extends Button implements Checkable { 17 | private boolean mChecked; 18 | 19 | public LikeButton(Context context) { 20 | super(context); 21 | } 22 | 23 | public LikeButton(Context context, AttributeSet attrs) { 24 | super(context, attrs); 25 | } 26 | 27 | public LikeButton(Context context, AttributeSet attrs, int defStyleAttr) { 28 | super(context, attrs, defStyleAttr); 29 | } 30 | 31 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 32 | public LikeButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 33 | super(context, attrs, defStyleAttr, defStyleRes); 34 | } 35 | 36 | @Override 37 | public void setChecked(boolean checked) { 38 | mChecked = checked; 39 | } 40 | 41 | @Override 42 | public boolean isChecked() { 43 | return mChecked; 44 | } 45 | 46 | @Override 47 | public void toggle() { 48 | mChecked = !mChecked; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/common/RxUtils.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.common; 2 | 3 | import java.io.IOException; 4 | 5 | import com.arellomobile.github.app.GithubError; 6 | 7 | import retrofit2.Call; 8 | import retrofit2.Response; 9 | import rx.Observable; 10 | import rx.Scheduler; 11 | import rx.android.schedulers.AndroidSchedulers; 12 | import rx.schedulers.Schedulers; 13 | 14 | /** 15 | * Date: 18.01.2016 16 | * Time: 14:37 17 | * 18 | * @author Yuri Shmakov 19 | */ 20 | public class RxUtils { 21 | public static Observable wrapRetrofitCall(final Call call) { 22 | return Observable.create(subscriber -> 23 | { 24 | final Response execute; 25 | try { 26 | execute = call.execute(); 27 | } catch (IOException e) { 28 | subscriber.onError(e); 29 | return; 30 | } 31 | 32 | if (execute.isSuccess()) { 33 | subscriber.onNext(execute.body()); 34 | } else { 35 | subscriber.onError(new GithubError(execute.errorBody())); 36 | } 37 | }); 38 | } 39 | 40 | public static Observable wrapAsync(Observable observable) { 41 | return wrapAsync(observable, Schedulers.io()); 42 | } 43 | 44 | public static Observable wrapAsync(Observable observable, Scheduler scheduler) { 45 | return observable 46 | .subscribeOn(scheduler) 47 | .observeOn(AndroidSchedulers.mainThread()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/presenters/RepositoryPresenter.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.presenters; 2 | 3 | import java.util.List; 4 | 5 | import com.arellomobile.github.mvp.models.Repository; 6 | import com.arellomobile.github.mvp.views.RepositoryView; 7 | import com.arellomobile.mvp.InjectViewState; 8 | import com.arellomobile.mvp.MvpPresenter; 9 | 10 | /** 11 | * Date: 27.01.2016 12 | * Time: 21:12 13 | * 14 | * @author Yuri Shmakov 15 | */ 16 | @InjectViewState 17 | public class RepositoryPresenter extends MvpPresenter { 18 | private boolean mIsFirstViewAttached = false; 19 | private Repository mRepository; 20 | private List mInProgress; 21 | private List mLikedIds; 22 | 23 | public void setRepository(Repository repository) { 24 | mRepository = repository; 25 | 26 | if (mIsFirstViewAttached) { 27 | getViewState().showRepository(repository); 28 | 29 | updateLikes(mInProgress, mLikedIds); 30 | } 31 | } 32 | 33 | @Override 34 | protected void onFirstViewAttach() { 35 | super.onFirstViewAttach(); 36 | 37 | mIsFirstViewAttached = true; 38 | 39 | setRepository(mRepository); 40 | } 41 | 42 | public void updateLikes(List inProgress, List likedIds) { 43 | mInProgress = inProgress; 44 | mLikedIds = likedIds; 45 | 46 | if (mRepository == null || mInProgress == null || mLikedIds == null) { 47 | return; 48 | } 49 | 50 | boolean isInProgress = inProgress.contains(mRepository.getId()); 51 | boolean isLiked = likedIds.contains(mRepository.getId()); 52 | 53 | getViewState().updateLike(isInProgress, isLiked); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/di/modules/RetrofitModule.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.di.modules; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import com.google.gson.FieldNamingPolicy; 6 | import com.google.gson.FieldNamingStrategy; 7 | import com.google.gson.Gson; 8 | import com.google.gson.GsonBuilder; 9 | 10 | import javax.inject.Singleton; 11 | 12 | import dagger.Module; 13 | import dagger.Provides; 14 | import retrofit2.Converter; 15 | import retrofit2.GsonConverterFactory; 16 | import retrofit2.Retrofit; 17 | 18 | /** 19 | * Date: 8/26/2016 20 | * Time: 12:28 21 | * 22 | * @author Artur Artikov 23 | */ 24 | @Module 25 | public class RetrofitModule { 26 | @Provides 27 | @Singleton 28 | public Retrofit provideRetrofit(Retrofit.Builder builder) { 29 | return builder.baseUrl("https://api.github.com").build(); 30 | } 31 | 32 | @Provides 33 | @Singleton 34 | public Retrofit.Builder provideRetrofitBuilder(Converter.Factory converterFactory) { 35 | return new Retrofit.Builder() 36 | .addConverterFactory(converterFactory); 37 | } 38 | 39 | @Provides 40 | @Singleton 41 | public Converter.Factory provideConverterFactory(Gson gson) { 42 | return GsonConverterFactory.create(gson); 43 | } 44 | 45 | @Provides 46 | @Singleton 47 | Gson provideGson() { 48 | return new GsonBuilder() 49 | .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE) 50 | .setFieldNamingStrategy(new CustomFieldNamingPolicy()) 51 | .setPrettyPrinting() 52 | .setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") 53 | .serializeNulls() 54 | .create(); 55 | } 56 | 57 | private static class CustomFieldNamingPolicy implements FieldNamingStrategy { 58 | @Override 59 | public String translateName(Field field) { 60 | String name = FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES.translateName(field); 61 | name = name.substring(2, name.length()).toLowerCase(); 62 | return name; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'me.tatarka.retrolambda' 3 | apply plugin: 'com.neenbedankt.android-apt' 4 | 5 | android { 6 | compileOptions { 7 | sourceCompatibility JavaVersion.VERSION_1_8 8 | targetCompatibility JavaVersion.VERSION_1_8 9 | } 10 | 11 | compileSdkVersion 24 12 | buildToolsVersion "23.0.3" 13 | 14 | defaultConfig { 15 | applicationId "com.arellomobile.github" 16 | minSdkVersion 14 17 | targetSdkVersion 24 18 | versionCode 1 19 | versionName "1.0" 20 | } 21 | 22 | buildTypes { 23 | release { 24 | minifyEnabled false 25 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 26 | } 27 | } 28 | 29 | lintOptions { 30 | abortOnError false 31 | } 32 | } 33 | 34 | dependencies { 35 | compile fileTree(dir: 'libs', include: ['*.jar']) 36 | testCompile 'junit:junit:4.12' 37 | compile 'com.android.support:appcompat-v7:24.2.1' 38 | compile 'com.android.support:design:24.2.1' 39 | 40 | compile 'com.j256.ormlite:ormlite-core:4.48' 41 | compile 'com.j256.ormlite:ormlite-android:4.48' 42 | 43 | compile 'com.google.code.gson:gson:2.5' 44 | 45 | compile 'com.jakewharton:butterknife:7.0.1' 46 | 47 | compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3' 48 | compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3' 49 | compile 'com.squareup.picasso:picasso:2.5.2' 50 | compile 'com.squareup:otto:1.3.8' 51 | 52 | compile 'io.reactivex:rxjava:1.1.0' 53 | compile 'io.reactivex:rxandroid:1.1.0' 54 | 55 | compile "com.google.dagger:dagger:2.5" 56 | provided 'org.glassfish:javax.annotation:10.0-b28' 57 | apt "com.google.dagger:dagger-compiler:2.5" 58 | 59 | compile 'com.arello-mobile:moxy:1.0.2' 60 | compile 'com.arello-mobile:moxy-app-compat:1.0.2' 61 | apt 'com.arello-mobile:moxy-compiler:1.0.2' 62 | } 63 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 21 | 22 | 26 | 27 | 31 | 32 | 38 | 39 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/presenters/SignInPresenter.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.presenters; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.util.Base64; 6 | 7 | import com.arellomobile.github.R; 8 | import com.arellomobile.github.app.GithubApp; 9 | import com.arellomobile.github.mvp.GithubService; 10 | import com.arellomobile.github.mvp.common.AuthUtils; 11 | import com.arellomobile.github.mvp.common.RxUtils; 12 | import com.arellomobile.github.mvp.models.User; 13 | import com.arellomobile.github.mvp.views.SignInView; 14 | import com.arellomobile.mvp.InjectViewState; 15 | import com.arellomobile.mvp.MvpPresenter; 16 | 17 | import javax.inject.Inject; 18 | 19 | import rx.Observable; 20 | 21 | /** 22 | * Date: 15.01.2016 23 | * Time: 18:33 24 | * 25 | * @author Yuri Shmakov 26 | */ 27 | @InjectViewState 28 | public class SignInPresenter extends MvpPresenter { 29 | 30 | @Inject 31 | Context mContext; 32 | @Inject 33 | GithubService mGithubService; 34 | 35 | public SignInPresenter() { 36 | GithubApp.getAppComponent().inject(this); 37 | } 38 | 39 | public void signIn(String email, String password) { 40 | 41 | Integer emailError = null; 42 | Integer passwordError = null; 43 | 44 | getViewState().showError(null, null); 45 | 46 | if (TextUtils.isEmpty(email)) { 47 | emailError = R.string.error_field_required; 48 | } 49 | 50 | if (TextUtils.isEmpty(password)) { 51 | passwordError = R.string.error_invalid_password; 52 | } 53 | 54 | if (emailError != null || passwordError != null) { 55 | getViewState().showError(emailError, passwordError); 56 | 57 | return; 58 | } 59 | 60 | getViewState().showProgress(); 61 | 62 | String credentials = String.format("%s:%s", email, password); 63 | 64 | final String token = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP); 65 | 66 | Observable userObservable = RxUtils.wrapRetrofitCall(mGithubService.signIn(token)) 67 | .doOnNext(user -> AuthUtils.setToken(token)); 68 | 69 | RxUtils.wrapAsync(userObservable) 70 | .subscribe(user -> { 71 | getViewState().hideProgress(); 72 | getViewState().successSignIn(); 73 | }, exception -> { 74 | getViewState().hideProgress(); 75 | getViewState().showError(exception.getMessage()); 76 | }); 77 | } 78 | 79 | public void onErrorCancel() { 80 | getViewState().hideError(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /app/src/main/java/com/arellomobile/github/mvp/models/User.java: -------------------------------------------------------------------------------- 1 | package com.arellomobile.github.mvp.models; 2 | 3 | import com.j256.ormlite.field.DatabaseField; 4 | import com.j256.ormlite.table.DatabaseTable; 5 | 6 | /** 7 | * Date: 18.01.2016 8 | * Time: 12:09 9 | * 10 | * @author Yuri Shmakov 11 | */ 12 | @DatabaseTable 13 | public class User { 14 | 15 | public static class Column { 16 | public static final String ID = "_id"; 17 | public static final String LOGIN = "login"; 18 | public static final String AVATAR = "avatar"; 19 | public static final String PUBLIC_REPOS = "public_repos"; 20 | public static final String PUBLIC_GISTS = "public_gists"; 21 | public static final String FOLLOWERS = "followers"; 22 | public static final String FOLLOWING = "following"; 23 | } 24 | 25 | @DatabaseField(columnName = Column.ID, id = true) 26 | private int mId; 27 | @DatabaseField(columnName = Column.LOGIN) 28 | private String mLogin; 29 | @DatabaseField(columnName = Column.AVATAR) 30 | private String mAvatarUrl; 31 | @DatabaseField(columnName = Column.PUBLIC_REPOS) 32 | private int mPublicRepos; 33 | @DatabaseField(columnName = Column.PUBLIC_GISTS) 34 | private int mPublicGists; 35 | @DatabaseField(columnName = Column.FOLLOWERS) 36 | private int mFollower; 37 | @DatabaseField(columnName = Column.FOLLOWING) 38 | private int mFollowing; 39 | 40 | public int getId() { 41 | return mId; 42 | } 43 | 44 | public void setId(int id) { 45 | mId = id; 46 | } 47 | 48 | public String getLogin() { 49 | return mLogin; 50 | } 51 | 52 | public void setLogin(String login) { 53 | mLogin = login; 54 | } 55 | 56 | public String getName() { 57 | return mLogin; 58 | } 59 | 60 | public void setName(String login) { 61 | mLogin = login; 62 | } 63 | 64 | public String getAvatarUrl() { 65 | return mAvatarUrl; 66 | } 67 | 68 | public void setAvatarUrl(String avatarUrl) { 69 | mAvatarUrl = avatarUrl; 70 | } 71 | 72 | public int getPublicRepos() { 73 | return mPublicRepos; 74 | } 75 | 76 | public void setPublicRepos(int publicRepos) { 77 | mPublicRepos = publicRepos; 78 | } 79 | 80 | public int getPublicGists() { 81 | return mPublicGists; 82 | } 83 | 84 | public void setPublicGists(int publicGists) { 85 | mPublicGists = publicGists; 86 | } 87 | 88 | public int getFollower() { 89 | return mFollower; 90 | } 91 | 92 | public void setFollower(int follower) { 93 | mFollower = follower; 94 | } 95 | 96 | public int getFollowing() { 97 | return mFollowing; 98 | } 99 | 100 | public void setFollowing(int following) { 101 | mFollowing = following; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_sign_in.xml: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 22 | 23 | 27 | 28 | 33 | 34 | 37 | 38 | 46 | 47 | 48 | 49 | 52 | 53 | 64 | 65 | 66 | 67 |