├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── app-mvp-dagger ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── example │ │ └── mvpdagger │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── example │ │ │ └── mvpdagger │ │ │ ├── AppApplication.java │ │ │ ├── AppComponent.java │ │ │ ├── AppModule.java │ │ │ ├── base │ │ │ ├── BaseActivity.java │ │ │ ├── BasePresenter.java │ │ │ ├── IPresenter.java │ │ │ └── IView.java │ │ │ ├── model │ │ │ ├── Repository.java │ │ │ └── User.java │ │ │ ├── network │ │ │ ├── ApiClient.java │ │ │ ├── ApiModule.java │ │ │ ├── ApiService.java │ │ │ └── RetrofitClient.java │ │ │ ├── repos │ │ │ ├── ReposActivity.java │ │ │ ├── ReposComponent.java │ │ │ ├── ReposContract.java │ │ │ ├── ReposPresenter.java │ │ │ ├── ReposPresenterModule.java │ │ │ └── RepositoryAdapter.java │ │ │ ├── scope │ │ │ ├── ActivityContext.java │ │ │ ├── ActivityScoped.java │ │ │ ├── ApplicationContext.java │ │ │ └── FragmentScoped.java │ │ │ ├── util │ │ │ ├── DrugDao.java │ │ │ ├── HttpMethod.java │ │ │ └── ToastUtil.java │ │ │ └── utils │ │ │ ├── NetUtil.java │ │ │ └── SDCardUtil.java │ └── res │ │ ├── layout │ │ ├── activity_repos.xml │ │ └── item_repo.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_search_white_36dp.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── example │ └── mvpdagger │ └── ExampleUnitTest.java ├── app-mvp ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pfh │ │ └── app_mvp │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── pfh │ │ │ └── app_mvp │ │ │ ├── MyApplication.java │ │ │ ├── adapter │ │ │ └── RepositoryAdapter.java │ │ │ ├── base │ │ │ ├── BaseActivity.java │ │ │ ├── BasePresenter.java │ │ │ └── IPresenter.java │ │ │ ├── model │ │ │ ├── Repository.java │ │ │ └── User.java │ │ │ ├── network │ │ │ ├── ApiClient.java │ │ │ ├── ApiService.java │ │ │ └── RetrofitClient.java │ │ │ ├── repos │ │ │ ├── ReposActivity.java │ │ │ ├── ReposContract.java │ │ │ └── ReposPresenter.java │ │ │ └── utils │ │ │ ├── NetUtil.java │ │ │ └── SDCardUtil.java │ └── res │ │ ├── layout │ │ ├── activity_repos.xml │ │ └── item_repo.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_search_white_36dp.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── pfh │ └── app_mvp │ └── ExampleUnitTest.java ├── app-mvvm ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pfh │ │ └── app_mvvm │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── pfh │ │ │ └── app_mvvm │ │ │ ├── MyApplication.java │ │ │ ├── adapter │ │ │ └── RepositoryAdapter.java │ │ │ ├── model │ │ │ ├── Repository.java │ │ │ └── User.java │ │ │ ├── network │ │ │ ├── ApiClient.java │ │ │ ├── ApiService.java │ │ │ ├── ExceptionHandler.java │ │ │ ├── HttpResult.java │ │ │ ├── HttpSubscriber.java │ │ │ └── RetrofitClient.java │ │ │ ├── utils │ │ │ ├── Constant.java │ │ │ ├── NetUtil.java │ │ │ └── SDCardUtil.java │ │ │ ├── view │ │ │ ├── MainActivity.java │ │ │ └── RepositoryActivity.java │ │ │ └── viewmodel │ │ │ ├── BaseViewModel.java │ │ │ ├── IViewModel.java │ │ │ ├── ItemRepoViewModel.java │ │ │ ├── MainViewModel.java │ │ │ └── RepositoryViewModel.java │ └── res │ │ ├── drawable-xhdpi │ │ └── ic_search_white_36dp.png │ │ ├── drawable │ │ ├── octocat.png │ │ └── placeholder.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_repository.xml │ │ └── item_repo.xml │ │ ├── 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 │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── pfh │ └── app_mvvm │ └── ExampleUnitTest.java ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── pfh │ │ └── architecture │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── pfh │ │ │ └── architecture │ │ │ └── MainActivity.java │ └── res │ │ ├── layout │ │ └── activity_main.xml │ │ ├── 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 │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── pfh │ └── architecture │ └── ExampleUnitTest.java ├── build.gradle ├── config.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 21 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | 47 | 48 | 49 | 50 | 1.8 51 | 52 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app-mvp-dagger/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app-mvp-dagger/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion "25.0.3" 7 | 8 | defaultConfig { 9 | applicationId "com.example.mvpdagger" 10 | minSdkVersion 15 11 | targetSdkVersion 25 12 | versionCode 1 13 | versionName "1.0" 14 | 15 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 16 | 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(dir: 'libs', include: ['*.jar']) 28 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 29 | exclude group: 'com.android.support', module: 'support-annotations' 30 | }) 31 | compile 'com.android.support:appcompat-v7:25.3.1' 32 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 33 | testCompile 'junit:junit:4.12' 34 | 35 | compile 'com.google.dagger:dagger:2.6' 36 | annotationProcessor 'com.google.dagger:dagger-compiler:2.6' 37 | 38 | Map dependencies = rootProject.ext.dependencies; 39 | 40 | compile dependencies.recyclerView 41 | compile dependencies.cardView 42 | compile dependencies.retrofit 43 | compile dependencies.retrofitConverterGson 44 | compile dependencies.retrofitAdapterRxJava 45 | compile dependencies.okhttp 46 | compile dependencies.okhttpLoggingInterceptor 47 | compile dependencies.rxAndroid 48 | compile dependencies.rxJava 49 | 50 | compile dependencies.butterknife 51 | 52 | } 53 | -------------------------------------------------------------------------------- /app-mvp-dagger/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 /Users/panfeihang/Library/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 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/androidTest/java/com/example/mvpdagger/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.mvpdagger", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/AppApplication.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger; 2 | 3 | import android.app.Application; 4 | 5 | import com.example.mvpdagger.network.ApiModule; 6 | 7 | /** 8 | * Created by panfeihang on 2017/6/9. 9 | */ 10 | 11 | public class AppApplication extends Application { 12 | 13 | private static AppApplication instance; 14 | 15 | AppComponent mAppComponent; 16 | 17 | @Override 18 | public void onCreate() { 19 | super.onCreate(); 20 | instance = this; 21 | mAppComponent = DaggerAppComponent.builder() 22 | .appModule(new AppModule(this)) 23 | .apiModule(new ApiModule()) 24 | .build(); 25 | } 26 | 27 | public static AppApplication getInstance() { 28 | return instance; 29 | } 30 | 31 | public AppComponent getAppComponent() { 32 | return mAppComponent; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/AppComponent.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger; 2 | 3 | import android.content.Context; 4 | 5 | import com.example.mvpdagger.network.ApiClient; 6 | import com.example.mvpdagger.network.ApiModule; 7 | import com.example.mvpdagger.scope.ApplicationContext; 8 | import com.example.mvpdagger.util.DrugDao; 9 | import com.example.mvpdagger.util.ToastUtil; 10 | 11 | import javax.inject.Singleton; 12 | 13 | import dagger.Component; 14 | 15 | /** 16 | * Created by panfeihang on 2017/6/9. 17 | */ 18 | 19 | @Singleton 20 | @Component(modules = {AppModule.class , ApiModule.class}) 21 | public interface AppComponent { 22 | 23 | void inject(AppApplication appApplication); 24 | 25 | @ApplicationContext 26 | Context getContext(); 27 | 28 | ToastUtil getToastUtil(); 29 | 30 | DrugDao getDrugDao(); 31 | 32 | ApiClient getApiClient(); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/AppModule.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger; 2 | 3 | import android.content.Context; 4 | 5 | import com.example.mvpdagger.scope.ApplicationContext; 6 | import com.example.mvpdagger.util.DrugDao; 7 | import com.example.mvpdagger.util.ToastUtil; 8 | 9 | import javax.inject.Singleton; 10 | 11 | import dagger.Module; 12 | import dagger.Provides; 13 | 14 | /** 15 | * Created by panfeihang on 2017/6/9. 16 | */ 17 | 18 | @Module 19 | public class AppModule { 20 | 21 | private final AppApplication mAppApplication; 22 | 23 | public AppModule(AppApplication mAppApplication) { 24 | this.mAppApplication = mAppApplication; 25 | } 26 | 27 | @Provides 28 | @Singleton 29 | AppApplication provideApplication() { 30 | return mAppApplication; 31 | } 32 | 33 | @Provides 34 | @ApplicationContext 35 | @Singleton 36 | Context provideContext() { 37 | return mAppApplication; 38 | } 39 | 40 | @Provides 41 | @Singleton 42 | ToastUtil provideToastUtils() { 43 | return new ToastUtil(mAppApplication); 44 | } 45 | 46 | @Provides 47 | @Singleton 48 | DrugDao provideDrugDao() { 49 | return new DrugDao(); 50 | } 51 | 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.base; 2 | 3 | import android.support.annotation.LayoutRes; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import com.example.mvpdagger.AppApplication; 7 | import com.example.mvpdagger.AppComponent; 8 | 9 | import javax.inject.Inject; 10 | 11 | import butterknife.ButterKnife; 12 | 13 | /** 14 | * Created by panfeihang on 2017/6/12. 15 | */ 16 | 17 | public class BaseActivity extends AppCompatActivity { 18 | 19 | @Inject 20 | public T mPresenter; 21 | 22 | @Override 23 | public void setContentView(@LayoutRes int layoutResID) { 24 | super.setContentView(layoutResID); 25 | ButterKnife.bind(this); 26 | } 27 | 28 | @Override 29 | protected void onDestroy() { 30 | super.onDestroy(); 31 | if (mPresenter != null) { 32 | mPresenter.detachView(); 33 | } 34 | } 35 | 36 | protected AppComponent getAppComponent(){ 37 | return AppApplication.getInstance().getAppComponent(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.base; 2 | 3 | import rx.Subscription; 4 | import rx.subscriptions.CompositeSubscription; 5 | 6 | /** 7 | * Created by panfeihang on 2017/6/12. 8 | */ 9 | 10 | public class BasePresenter implements IPresenter { 11 | 12 | protected T mView; 13 | protected CompositeSubscription mCompositeSubscription; 14 | 15 | protected void unsubscribe() { 16 | if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) { 17 | mCompositeSubscription.unsubscribe(); 18 | } 19 | } 20 | 21 | protected void addSubscription(Subscription subscription) { 22 | if (mCompositeSubscription == null) { 23 | mCompositeSubscription = new CompositeSubscription(); 24 | } 25 | mCompositeSubscription.add(subscription); 26 | } 27 | 28 | @Override 29 | public void detachView() { 30 | this.mView = null; 31 | unsubscribe(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/base/IPresenter.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.base; 2 | 3 | /** 4 | * Presenter基类 5 | */ 6 | public interface IPresenter{ 7 | 8 | 9 | 10 | void detachView(); 11 | } 12 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/base/IView.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.base; 2 | 3 | /** 4 | * Created by panfeihang on 2017/6/12. 5 | */ 6 | 7 | public interface IView { 8 | } 9 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/model/User.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | public class User implements Parcelable { 9 | public long id; 10 | public String name; 11 | public String url; 12 | public String email; 13 | public String login; 14 | public String location; 15 | @SerializedName("avatar_url") 16 | public String avatarUrl; 17 | 18 | public User() { 19 | } 20 | 21 | public boolean hasEmail() { 22 | return email != null && !email.isEmpty(); 23 | } 24 | 25 | public boolean hasLocation() { 26 | return location != null && !location.isEmpty(); 27 | } 28 | 29 | @Override 30 | public int describeContents() { 31 | return 0; 32 | } 33 | 34 | @Override 35 | public void writeToParcel(Parcel dest, int flags) { 36 | dest.writeLong(this.id); 37 | dest.writeString(this.name); 38 | dest.writeString(this.url); 39 | dest.writeString(this.email); 40 | dest.writeString(this.login); 41 | dest.writeString(this.location); 42 | dest.writeString(this.avatarUrl); 43 | } 44 | 45 | protected User(Parcel in) { 46 | this.id = in.readLong(); 47 | this.name = in.readString(); 48 | this.url = in.readString(); 49 | this.email = in.readString(); 50 | this.login = in.readString(); 51 | this.location = in.readString(); 52 | this.avatarUrl = in.readString(); 53 | } 54 | 55 | public static final Creator CREATOR = new Creator() { 56 | public User createFromParcel(Parcel source) { 57 | return new User(source); 58 | } 59 | 60 | public User[] newArray(int size) { 61 | return new User[size]; 62 | } 63 | }; 64 | 65 | @Override 66 | public boolean equals(Object o) { 67 | if (this == o) return true; 68 | if (o == null || getClass() != o.getClass()) return false; 69 | 70 | User user = (User) o; 71 | 72 | if (id != user.id) return false; 73 | if (name != null ? !name.equals(user.name) : user.name != null) return false; 74 | if (url != null ? !url.equals(user.url) : user.url != null) return false; 75 | if (email != null ? !email.equals(user.email) : user.email != null) return false; 76 | if (login != null ? !login.equals(user.login) : user.login != null) return false; 77 | if (location != null ? !location.equals(user.location) : user.location != null) 78 | return false; 79 | return !(avatarUrl != null ? !avatarUrl.equals(user.avatarUrl) : user.avatarUrl != null); 80 | 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | int result = (int) (id ^ (id >>> 32)); 86 | result = 31 * result + (name != null ? name.hashCode() : 0); 87 | result = 31 * result + (url != null ? url.hashCode() : 0); 88 | result = 31 * result + (email != null ? email.hashCode() : 0); 89 | result = 31 * result + (login != null ? login.hashCode() : 0); 90 | result = 31 * result + (location != null ? location.hashCode() : 0); 91 | result = 31 * result + (avatarUrl != null ? avatarUrl.hashCode() : 0); 92 | return result; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/network/ApiClient.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.network; 2 | 3 | 4 | import com.example.mvpdagger.model.Repository; 5 | import com.example.mvpdagger.model.User; 6 | 7 | import java.util.List; 8 | 9 | import javax.inject.Inject; 10 | 11 | import rx.Observable; 12 | import rx.Subscriber; 13 | import rx.Subscription; 14 | 15 | 16 | public class ApiClient extends RetrofitClient { 17 | 18 | @Inject 19 | public ApiClient(){ 20 | init(); 21 | } 22 | // public static ApiClient getInstance(){ 23 | // return SingletonHolder.INSTANCE; 24 | // } 25 | // 26 | // private static class SingletonHolder{ 27 | // private static final ApiClient INSTANCE = new ApiClient(); 28 | // } 29 | 30 | //****所有具体的网络请求方法都要在下面注册,方便后期维护和测试、替换网络框架****// 31 | 32 | public Observable> getRepositories(String username){ 33 | return apiService.publicRepositories(username) 34 | .compose(defaultSchedulers); 35 | } 36 | 37 | public Subscription userFromUrl(String userUrl, Subscriber subscriber){ 38 | return apiService.userFromUrl(userUrl) 39 | .compose(defaultSchedulers) 40 | .subscribe(subscriber); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/network/ApiModule.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.network; 2 | 3 | import javax.inject.Singleton; 4 | 5 | import dagger.Module; 6 | import dagger.Provides; 7 | 8 | /** 9 | * Created by panfeihang on 2017/6/12. 10 | */ 11 | 12 | @Module 13 | public class ApiModule { 14 | 15 | @Provides 16 | @Singleton 17 | ApiClient provideApiClient() { 18 | return new ApiClient(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/network/ApiService.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.network; 2 | 3 | 4 | import com.example.mvpdagger.model.Repository; 5 | import com.example.mvpdagger.model.User; 6 | 7 | import java.util.List; 8 | 9 | import retrofit2.http.GET; 10 | import retrofit2.http.Path; 11 | import retrofit2.http.Url; 12 | import rx.Observable; 13 | 14 | /** 15 | * 可以把所有api都放到一个接口下,如ApiStores 16 | * 也可以按照功能划分,多些几个接口,如GitHubService,XxxService, 17 | */ 18 | 19 | public interface ApiService { 20 | 21 | @GET("users/{username}/repos") 22 | Observable> publicRepositories(@Path("username") String username); 23 | 24 | @GET 25 | Observable userFromUrl(@Url String userUrl); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/network/RetrofitClient.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.network; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.example.mvpdagger.AppApplication; 7 | import com.example.mvpdagger.BuildConfig; 8 | import com.example.mvpdagger.utils.NetUtil; 9 | import com.example.mvpdagger.utils.SDCardUtil; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import okhttp3.Cache; 16 | import okhttp3.CacheControl; 17 | import okhttp3.ConnectionPool; 18 | import okhttp3.Interceptor; 19 | import okhttp3.OkHttpClient; 20 | import okhttp3.Request; 21 | import okhttp3.Response; 22 | import okhttp3.logging.HttpLoggingInterceptor; 23 | import retrofit2.Retrofit; 24 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 25 | import retrofit2.converter.gson.GsonConverterFactory; 26 | import rx.Observable; 27 | import rx.android.schedulers.AndroidSchedulers; 28 | import rx.schedulers.Schedulers; 29 | 30 | 31 | public class RetrofitClient { 32 | 33 | private static final String BASE_URL = "https://api.github.com/"; 34 | private static final int maxCacheSize = 5; // MB 35 | 36 | protected ApiService apiService; 37 | private static Retrofit retrofit; 38 | private static OkHttpClient okHttpClient; 39 | private static Context mContext; 40 | 41 | protected Observable.Transformer defaultSchedulers = new Observable.Transformer() { 42 | @Override 43 | public Object call(Object observable) { 44 | return ((Observable) observable).subscribeOn(Schedulers.io()) 45 | .unsubscribeOn(Schedulers.io()) 46 | .observeOn(AndroidSchedulers.mainThread()); 47 | } 48 | }; 49 | 50 | public ApiService getApiService(){ 51 | if (apiService == null){ 52 | apiService = retrofit.create(ApiService.class); 53 | } 54 | return apiService; 55 | } 56 | 57 | protected void init(){ 58 | this.mContext = AppApplication.getInstance(); 59 | initOkHttpClient(); 60 | initRetrofit(); 61 | if (apiService == null){ 62 | apiService = retrofit.create(ApiService.class); 63 | } 64 | } 65 | 66 | private void initRetrofit() { 67 | retrofit = new Retrofit.Builder() 68 | .baseUrl(BASE_URL) 69 | .client(okHttpClient) 70 | .addConverterFactory(GsonConverterFactory.create()) 71 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 72 | .build(); 73 | } 74 | 75 | private void initOkHttpClient(){ 76 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 77 | //打印请求log日志 78 | if (BuildConfig.DEBUG) { 79 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); 80 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 81 | builder.addInterceptor(loggingInterceptor); 82 | } 83 | //缓存 84 | File cacheFile = SDCardUtil.getCacheDir(mContext,"httpCache"); 85 | Cache cache = new Cache(cacheFile, 1024 * 1024 * maxCacheSize); 86 | Interceptor cacheInterceptor = new Interceptor() { 87 | @Override 88 | public Response intercept(Chain chain) throws IOException { 89 | Request request = chain.request(); 90 | if (!NetUtil.isConnected(mContext)){ 91 | request = request.newBuilder() 92 | .cacheControl(CacheControl.FORCE_CACHE) 93 | .build(); 94 | Log.d("OkHttp", "网络不可用请求拦截"); 95 | } 96 | Response response = chain.proceed(request); 97 | 98 | if (NetUtil.isConnected(mContext)){ 99 | int maxAge = 60; // 有网络 缓存60s 100 | response = response.newBuilder() 101 | //覆盖服务器响应头的Cache-Control,用我们自己的,因为服务器响应回来的可能不支持缓存 102 | .header("Cache-Control", "public,max-age="+maxAge) 103 | .removeHeader("Pragma") 104 | .build(); 105 | }else { 106 | // 无网络时,设置超时为4周 107 | int maxAge = 60 * 60 * 24 * 28; 108 | response.newBuilder() 109 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxAge) 110 | .removeHeader("Pragma") 111 | .build(); 112 | } 113 | return response; 114 | } 115 | }; 116 | builder.cache(cache).addInterceptor(cacheInterceptor); 117 | //设置超时 118 | builder.connectTimeout(15, TimeUnit.SECONDS); 119 | builder.readTimeout(20, TimeUnit.SECONDS); 120 | builder.writeTimeout(20, TimeUnit.SECONDS); 121 | //错误重连 122 | builder.retryOnConnectionFailure(true); 123 | builder.connectionPool(new ConnectionPool(8, 15, TimeUnit.SECONDS)); 124 | okHttpClient = builder.build(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/repos/ReposActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.repos; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.widget.LinearLayoutManager; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.text.Editable; 7 | import android.text.TextWatcher; 8 | import android.view.View; 9 | import android.widget.EditText; 10 | import android.widget.ImageButton; 11 | import android.widget.ProgressBar; 12 | import android.widget.TextView; 13 | 14 | import com.example.mvpdagger.R; 15 | import com.example.mvpdagger.base.BaseActivity; 16 | import com.example.mvpdagger.model.Repository; 17 | 18 | import java.util.List; 19 | 20 | import butterknife.Bind; 21 | 22 | public class ReposActivity extends BaseActivity implements ReposContract.View { 23 | 24 | @Bind(R.id.button_search) 25 | ImageButton button_search; 26 | @Bind(R.id.edit_text_username) 27 | EditText edit_text_username; 28 | @Bind(R.id.progress) 29 | ProgressBar progress; 30 | @Bind(R.id.text_info) 31 | TextView text_info; 32 | @Bind(R.id.repos_recycler_view) 33 | RecyclerView repos_recycler_view; 34 | 35 | // @Inject 36 | // ReposContract.Presenter mPresenter; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_repos); 42 | DaggerReposComponent.builder() 43 | .appComponent(getAppComponent()) 44 | .reposPresenterModule(new ReposPresenterModule(ReposActivity.this)) 45 | .build() 46 | .inject(this); 47 | edit_text_username.addTextChangedListener(new TextWatcher() { 48 | @Override 49 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 50 | 51 | } 52 | 53 | @Override 54 | public void onTextChanged(CharSequence s, int start, int before, int count) { 55 | button_search.setVisibility(s.toString().length() > 0 ? View.VISIBLE : View.GONE); 56 | } 57 | 58 | @Override 59 | public void afterTextChanged(Editable s) { 60 | 61 | } 62 | }); 63 | 64 | button_search.setOnClickListener(new View.OnClickListener() { 65 | @Override 66 | public void onClick(View v) { 67 | 68 | mPresenter.loadRepos(edit_text_username.getText().toString()); 69 | } 70 | }); 71 | setupRecyclerView(); 72 | 73 | } 74 | 75 | private void setupRecyclerView() { 76 | RepositoryAdapter adapter = new RepositoryAdapter(); 77 | repos_recycler_view.setAdapter(adapter); 78 | repos_recycler_view.setLayoutManager(new LinearLayoutManager(this)); 79 | } 80 | 81 | @Override 82 | public void showProgress() { 83 | progress.setVisibility(View.VISIBLE); 84 | } 85 | 86 | @Override 87 | public void hideProgress() { 88 | progress.setVisibility(View.GONE); 89 | } 90 | 91 | @Override 92 | public void showLoadErrorMsg(String errorInfo) { 93 | text_info.setVisibility(View.VISIBLE); 94 | text_info.setText(errorInfo); 95 | } 96 | 97 | @Override 98 | public void hideLoadErrorMsg() { 99 | text_info.setVisibility(View.GONE); 100 | } 101 | 102 | @Override 103 | public void showRepos(List repositories) { 104 | repos_recycler_view.setVisibility(View.VISIBLE); 105 | RepositoryAdapter adapter = (RepositoryAdapter) repos_recycler_view.getAdapter(); 106 | adapter.setRepositories(repositories); 107 | adapter.notifyDataSetChanged(); 108 | } 109 | 110 | @Override 111 | public void hideRepos() { 112 | repos_recycler_view.setVisibility(View.GONE); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/repos/ReposComponent.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.repos; 2 | 3 | import com.example.mvpdagger.AppComponent; 4 | import com.example.mvpdagger.scope.ActivityScoped; 5 | 6 | import dagger.Component; 7 | 8 | /** 9 | * Created by panfeihang on 2017/6/12. 10 | */ 11 | 12 | @ActivityScoped 13 | @Component(dependencies = AppComponent.class,modules = ReposPresenterModule.class) 14 | public interface ReposComponent { 15 | 16 | void inject(ReposActivity reposActivity); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/repos/ReposContract.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.repos; 2 | 3 | import com.example.mvpdagger.base.IPresenter; 4 | import com.example.mvpdagger.base.IView; 5 | import com.example.mvpdagger.model.Repository; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * Created by panfeihang on 2017/6/12. 11 | */ 12 | 13 | public interface ReposContract { 14 | 15 | interface View extends IView { 16 | 17 | void showProgress(); 18 | 19 | void hideProgress(); 20 | 21 | void showLoadErrorMsg(String errorInfo); 22 | 23 | void hideLoadErrorMsg(); 24 | 25 | void showRepos(List repositories); 26 | 27 | void hideRepos(); 28 | 29 | } 30 | 31 | interface Presenter extends IPresenter { 32 | 33 | void loadRepos(String username); 34 | 35 | void clickRepository(); 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/repos/ReposPresenter.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.repos; 2 | 3 | import com.example.mvpdagger.base.BasePresenter; 4 | import com.example.mvpdagger.model.Repository; 5 | import com.example.mvpdagger.network.ApiClient; 6 | 7 | import java.util.List; 8 | 9 | import javax.inject.Inject; 10 | 11 | import rx.Subscriber; 12 | 13 | /** 14 | * Created by panfeihang on 2017/6/12. 15 | */ 16 | 17 | public class ReposPresenter extends BasePresenter implements ReposContract.Presenter { 18 | 19 | //private ReposContract.View mView; 20 | private ApiClient mApiClient; 21 | 22 | @Inject 23 | public ReposPresenter(ReposContract.View mView, ApiClient mApiClient) { 24 | this.mView = mView; 25 | this.mApiClient = mApiClient; 26 | } 27 | 28 | @Override 29 | public void loadRepos(String username) { 30 | mView.showProgress(); 31 | mView.hideLoadErrorMsg(); 32 | mView.hideRepos(); 33 | 34 | mApiClient.getRepositories(username) 35 | .subscribe(new Subscriber>() { 36 | @Override 37 | public void onCompleted() { 38 | mView.hideLoadErrorMsg(); 39 | mView.hideProgress(); 40 | } 41 | 42 | @Override 43 | public void onError(Throwable e) { 44 | mView.hideProgress(); 45 | mView.hideRepos(); 46 | mView.showLoadErrorMsg(e.getMessage()); 47 | } 48 | 49 | @Override 50 | public void onNext(List repositories) { 51 | mView.showRepos(repositories); 52 | } 53 | }); 54 | 55 | } 56 | 57 | @Override 58 | public void clickRepository() { 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/repos/ReposPresenterModule.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.repos; 2 | 3 | import com.example.mvpdagger.scope.ActivityScoped; 4 | 5 | import dagger.Module; 6 | import dagger.Provides; 7 | 8 | /** 9 | * Created by panfeihang on 2017/6/12. 10 | */ 11 | 12 | @Module 13 | public class ReposPresenterModule { 14 | 15 | private ReposContract.View mView; 16 | 17 | public ReposPresenterModule(ReposContract.View mView) { 18 | this.mView = mView; 19 | } 20 | 21 | @ActivityScoped 22 | @Provides 23 | ReposContract.View provideView(){ 24 | return mView; 25 | } 26 | 27 | // @ActivityScoped 28 | // @Provides 29 | // ReposContract.Presenter providePresenter(ReposContract.View view , ApiClient apiClient) { 30 | // return new ReposPresenter(view,apiClient); 31 | // } 32 | } 33 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/repos/RepositoryAdapter.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.repos; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import com.example.mvpdagger.R; 10 | import com.example.mvpdagger.model.Repository; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | import butterknife.Bind; 16 | import butterknife.ButterKnife; 17 | 18 | 19 | public class RepositoryAdapter extends RecyclerView.Adapter { 20 | 21 | private List repositories; 22 | 23 | public RepositoryAdapter() { 24 | this.repositories = Collections.emptyList(); 25 | } 26 | 27 | public RepositoryAdapter(List repositories) { 28 | this.repositories = repositories; 29 | } 30 | 31 | public void setRepositories(List repositories) { 32 | this.repositories = repositories; 33 | } 34 | 35 | 36 | @Override 37 | public RepositoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 38 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_repo, parent, false); 39 | return new RepositoryViewHolder(view); 40 | } 41 | 42 | @Override 43 | public void onBindViewHolder(RepositoryViewHolder holder, int position) { 44 | Repository repository = repositories.get(position); 45 | holder.text_repo_title.setText(repository.getName()); 46 | holder.text_repo_description.setText(repository.getDescription()); 47 | holder.text_watchers.setText(repository.getWatchers()+""); 48 | holder.text_forks.setText(repository.getForks()+""); 49 | holder.text_stars.setText(repository.getStars()+""); 50 | } 51 | 52 | @Override 53 | public int getItemCount() { 54 | return repositories.size(); 55 | } 56 | 57 | static class RepositoryViewHolder extends RecyclerView.ViewHolder{ 58 | 59 | @Bind(R.id.text_repo_title) 60 | TextView text_repo_title; 61 | @Bind(R.id.text_repo_description) 62 | TextView text_repo_description; 63 | @Bind(R.id.text_watchers) 64 | TextView text_watchers; 65 | @Bind(R.id.text_forks) 66 | TextView text_forks; 67 | @Bind(R.id.text_stars) 68 | TextView text_stars; 69 | 70 | public RepositoryViewHolder(View itemView) { 71 | super(itemView); 72 | ButterKnife.bind(this,itemView); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/scope/ActivityContext.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.scope; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | import javax.inject.Qualifier; 8 | 9 | /** 10 | * Identifies for activity context 11 | */ 12 | @Qualifier 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface ActivityContext { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/scope/ActivityScoped.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.scope; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | import javax.inject.Scope; 8 | 9 | /** 10 | * different signal with {@link javax.inject.Singleton} 11 | * Singleton Identifies a type that the injector only instantiates once. Not inherited. 12 | */ 13 | @Documented 14 | @Scope 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface ActivityScoped { 17 | } 18 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/scope/ApplicationContext.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.scope; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | import javax.inject.Qualifier; 8 | 9 | /** 10 | * Identifies for application context 11 | */ 12 | @Qualifier 13 | @Documented 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface ApplicationContext { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/scope/FragmentScoped.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.scope; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | 7 | import javax.inject.Scope; 8 | 9 | /** 10 | * different signal with {@link javax.inject.Singleton} 11 | * Singleton Identifies a type that the injector only instantiates once. Not inherited. 12 | */ 13 | @Documented 14 | @Scope 15 | @Retention(RetentionPolicy.RUNTIME) 16 | public @interface FragmentScoped { 17 | } 18 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/util/DrugDao.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.util; 2 | 3 | /** 4 | * Created by panfeihang on 2017/6/9. 5 | */ 6 | 7 | public class DrugDao { 8 | } 9 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/util/HttpMethod.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.util; 2 | 3 | /** 4 | * Created by panfeihang on 2017/6/9. 5 | */ 6 | 7 | public class HttpMethod { 8 | } 9 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/util/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.util; 2 | 3 | import android.content.Context; 4 | import android.widget.Toast; 5 | 6 | /** 7 | * ToastUtils 8 | * 9 | * 10 | */ 11 | public class ToastUtil { 12 | private Context mContext; 13 | private static ToastUtil mInstance; 14 | private Toast mToast; 15 | 16 | // public static ToastUtils getInstance() { 17 | // return mInstance; 18 | // } 19 | // 20 | // public static void initialize(Context ctx) { 21 | // mInstance = new ToastUtils(ctx); 22 | // } 23 | 24 | public ToastUtil(Context ctx) { 25 | mContext = ctx; 26 | } 27 | 28 | public void showToast(String text) { 29 | if(mToast == null) { 30 | mToast = Toast.makeText(mContext, text, Toast.LENGTH_LONG); 31 | } else { 32 | mToast.setText(text); 33 | mToast.setDuration(Toast.LENGTH_LONG); 34 | } 35 | mToast.show(); 36 | } 37 | 38 | public void cancelToast() { 39 | if (mToast != null) { 40 | mToast.cancel(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/utils/NetUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.ComponentName; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.ConnectivityManager; 8 | import android.net.NetworkInfo; 9 | 10 | /** 11 | * 网络相关辅助类 12 | */ 13 | public class NetUtil { 14 | 15 | private NetUtil() { 16 | /* cannot be instantiated */ 17 | throw new UnsupportedOperationException("cannot be instantiated"); 18 | } 19 | 20 | /** 21 | * 判断网络是否连接 22 | * 23 | * @param context 24 | * @return 25 | */ 26 | public static boolean isConnected(Context context) { 27 | ConnectivityManager connectivityManager = (ConnectivityManager) context 28 | .getSystemService(Context.CONNECTIVITY_SERVICE); 29 | if (null != connectivityManager) { 30 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 31 | if (networkInfo != null){ 32 | return networkInfo.isAvailable(); 33 | } 34 | } 35 | return false; 36 | } 37 | 38 | /** 39 | * 判断是否是WIFI连接 40 | * 41 | * @param context 42 | * @return 43 | */ 44 | public static boolean isWIFI(Context context) { 45 | 46 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 47 | if (connectivityManager == null) 48 | return false; 49 | return connectivityManager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI; 50 | } 51 | 52 | /** 53 | * 打开网络设置界面 54 | */ 55 | public static void openSetting(Activity activity) { 56 | Intent intent = new Intent("/"); 57 | ComponentName cm = new ComponentName("com.android.settings", 58 | "com.android.settings.WirelessSettings"); 59 | intent.setComponent(cm); 60 | intent.setAction("android.intent.action.VIEW"); 61 | activity.startActivityForResult(intent, 0); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/java/com/example/mvpdagger/utils/SDCardUtil.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger.utils; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.os.StatFs; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * SD卡辅助类 11 | */ 12 | public class SDCardUtil { 13 | 14 | private SDCardUtil() { 15 | /* cannot be instantiated */ 16 | throw new UnsupportedOperationException("cannot be instantiated"); 17 | } 18 | 19 | /** 20 | * 判断SDCard是否可用 21 | * 22 | * @return 23 | */ 24 | public static boolean isSDCardEnable() { 25 | return Environment.getExternalStorageState().equals( 26 | Environment.MEDIA_MOUNTED); 27 | } 28 | 29 | /** 30 | * 获取SD卡路径 31 | * 32 | * @return 33 | */ 34 | public static String getSDCardPath() { 35 | return Environment.getExternalStorageDirectory().getAbsolutePath() 36 | + File.separator; 37 | } 38 | 39 | /** 40 | * 获取SD卡的剩余容量 单位byte 41 | * 42 | * @return 43 | */ 44 | public static long getSDCardAllSize() { 45 | if (isSDCardEnable()) { 46 | StatFs stat = new StatFs(getSDCardPath()); 47 | // 获取空闲的数据块的数量 48 | long availableBlocks = (long) stat.getAvailableBlocks() - 4; 49 | // 获取单个数据块的大小(byte) 50 | long freeBlocks = stat.getAvailableBlocks(); 51 | return freeBlocks * availableBlocks; 52 | } 53 | return 0; 54 | } 55 | 56 | /** 57 | * 获取指定路径所在空间的剩余可用容量字节数,单位byte 58 | * 59 | * @param filePath 60 | * @return 容量字节 SDCard可用空间,内部存储可用空间 61 | */ 62 | public static long getFreeBytes(String filePath) 63 | { 64 | // 如果是sd卡的下的路径,则获取sd卡可用容量 65 | if (filePath.startsWith(getSDCardPath())) { 66 | filePath = getSDCardPath(); 67 | } else { 68 | // 如果是内部存储的路径,则获取内存存储的可用容量 69 | filePath = Environment.getDataDirectory().getAbsolutePath(); 70 | } 71 | StatFs stat = new StatFs(filePath); 72 | long availableBlocks = (long) stat.getAvailableBlocks() - 4; 73 | return stat.getBlockSize() * availableBlocks; 74 | } 75 | 76 | /** 77 | * 获取系统存储路径 78 | * 79 | * @return 80 | */ 81 | public static String getRootDirectoryPath() { 82 | return Environment.getRootDirectory().getAbsolutePath(); 83 | } 84 | 85 | 86 | /** 87 | * 获取缓存路径,存储临时文件,可被一键清理和卸载清理 88 | * 当SD卡存在或者SD卡不可被移除的时候, 89 | * 就调用getExternalCacheDir()方法来获取缓存路径, 90 | * 否则就调用getCacheDir()方法来获取缓存路径。 91 | * 前者获取到的就是/sdcard/Android/data//cache 这个路径, 92 | * 而后者获取到的是 /data/data//cache 这个路径。 93 | * @param context 94 | * @param uniqueName 95 | * @return 96 | */ 97 | public static File getCacheDir(Context context, String uniqueName) { 98 | String cachePath; 99 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 100 | || !Environment.isExternalStorageRemovable()) { 101 | cachePath = context.getExternalCacheDir().getPath(); 102 | } else { 103 | cachePath = context.getCacheDir().getPath(); 104 | } 105 | return new File(cachePath + File.separator + uniqueName); 106 | } 107 | 108 | /*返回缓存路径*/ 109 | public static File getCacheDir(Context context) { 110 | String cachePath; 111 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 112 | || !Environment.isExternalStorageRemovable()) { 113 | cachePath = context.getExternalCacheDir().getPath(); 114 | } else { 115 | cachePath = context.getCacheDir().getPath(); 116 | } 117 | return new File(cachePath); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/layout/activity_repos.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 29 | 30 | 39 | 40 | 50 | 51 | 52 | 60 | 61 | 74 | 75 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/layout/item_repo.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 20 | 21 | 32 | 33 | 44 | 45 | 49 | 50 | 54 | 55 | 63 | 64 | 72 | 73 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-hdpi/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-hdpi/ic_search_white_36dp.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp-dagger/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #000000 8 | #ffffff 9 | #75ffffff 10 | #e1e1e1 11 | #3F51B5 12 | #303F9F 13 | #C5CAE9 14 | #03A9F4 15 | #212121 16 | #727272 17 | #FFFFFF 18 | #cbcbcb 19 | 20 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 12dp 6 | 12dp 7 | 6dp 8 | 6dp 9 | 10 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | app-mvp-dagger 3 | enter your name 4 | 5 | 6 | Settings 7 | %d \nStars 8 | %d \nWatchers 9 | %d \nForks 10 | Oops, something went wrong 11 | This account doesn\'t have any public repository 12 | Oops, Octocat doesn\'t know that username 13 | GitHub username 14 | Enter a GitHub username above to see its repositories 15 | This repository is a fork 16 | Language: %s 17 | 18 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app-mvp-dagger/src/test/java/com/example/mvpdagger/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.mvpdagger; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app-mvp/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app-mvp/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.0" 6 | 7 | defaultConfig { 8 | applicationId "com.pfh.app_mvp" 9 | minSdkVersion 14 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | compile fileTree(dir: 'libs', include: ['*.jar']) 27 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 28 | exclude group: 'com.android.support', module: 'support-annotations' 29 | }) 30 | compile 'com.android.support:appcompat-v7:25.3.1' 31 | testCompile 'junit:junit:4.12' 32 | 33 | Map dependencies = rootProject.ext.dependencies; 34 | 35 | compile dependencies.appCompat 36 | compile dependencies.cardView 37 | compile dependencies.recyclerView 38 | compile dependencies.retrofit 39 | compile dependencies.retrofitConverterGson 40 | compile dependencies.retrofitAdapterRxJava 41 | compile dependencies.okhttp 42 | compile dependencies.okhttpLoggingInterceptor 43 | compile dependencies.picasso 44 | compile dependencies.rxAndroid 45 | compile dependencies.circleImageView 46 | compile dependencies.butterknife 47 | 48 | testCompile dependencies.jUnit 49 | testCompile dependencies.mockito 50 | testCompile dependencies.robolectric 51 | } 52 | -------------------------------------------------------------------------------- /app-mvp/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:\Users\afayp\AppData\Local\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-mvp/src/androidTest/java/com/pfh/app_mvp/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.pfh.app_mvp", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app-mvp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp; 2 | 3 | import android.app.Application; 4 | 5 | 6 | public class MyApplication extends Application { 7 | 8 | private static MyApplication instance; 9 | 10 | public static MyApplication getInstance() { 11 | return instance; 12 | } 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | instance = this; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/adapter/RepositoryAdapter.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.adapter; 2 | 3 | import android.support.v7.widget.RecyclerView; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import com.pfh.app_mvp.R; 10 | import com.pfh.app_mvp.model.Repository; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | 15 | import butterknife.Bind; 16 | import butterknife.ButterKnife; 17 | 18 | 19 | public class RepositoryAdapter extends RecyclerView.Adapter { 20 | 21 | private List repositories; 22 | 23 | public RepositoryAdapter() { 24 | this.repositories = Collections.emptyList(); 25 | } 26 | 27 | public RepositoryAdapter(List repositories) { 28 | this.repositories = repositories; 29 | } 30 | 31 | public void setRepositories(List repositories) { 32 | this.repositories = repositories; 33 | } 34 | 35 | 36 | @Override 37 | public RepositoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 38 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_repo, parent, false); 39 | return new RepositoryViewHolder(view); 40 | } 41 | 42 | @Override 43 | public void onBindViewHolder(RepositoryViewHolder holder, int position) { 44 | Repository repository = repositories.get(position); 45 | holder.text_repo_title.setText(repository.getName()); 46 | holder.text_repo_description.setText(repository.getDescription()); 47 | holder.text_watchers.setText(repository.getWatchers()+""); 48 | holder.text_forks.setText(repository.getForks()+""); 49 | holder.text_stars.setText(repository.getStars()+""); 50 | } 51 | 52 | @Override 53 | public int getItemCount() { 54 | return repositories.size(); 55 | } 56 | 57 | static class RepositoryViewHolder extends RecyclerView.ViewHolder{ 58 | 59 | @Bind(R.id.text_repo_title) 60 | TextView text_repo_title; 61 | @Bind(R.id.text_repo_description) 62 | TextView text_repo_description; 63 | @Bind(R.id.text_watchers) 64 | TextView text_watchers; 65 | @Bind(R.id.text_forks) 66 | TextView text_forks; 67 | @Bind(R.id.text_stars) 68 | TextView text_stars; 69 | 70 | public RepositoryViewHolder(View itemView) { 71 | super(itemView); 72 | ButterKnife.bind(this,itemView); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/base/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.base; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.LayoutRes; 6 | import android.support.annotation.Nullable; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.view.inputmethod.InputMethodManager; 9 | 10 | import butterknife.ButterKnife; 11 | 12 | /** 13 | * 14 | */ 15 | 16 | public abstract class BaseActivity

extends AppCompatActivity { 17 | 18 | protected P mPresenter; 19 | 20 | @Override 21 | protected void onCreate(@Nullable Bundle savedInstanceState) { 22 | mPresenter = createPresenter(); 23 | super.onCreate(savedInstanceState); 24 | } 25 | 26 | @Override 27 | public void setContentView(@LayoutRes int layoutResID) { 28 | super.setContentView(layoutResID); 29 | ButterKnife.bind(this); 30 | } 31 | 32 | @Override 33 | protected void onDestroy() { 34 | if (mPresenter != null){ 35 | mPresenter.detachView(); 36 | } 37 | super.onDestroy(); 38 | } 39 | 40 | protected abstract P createPresenter(); 41 | 42 | protected void hideSoftKeyboard(){ 43 | InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE); //得到InputMethodManager的实例 44 | if (imm.isActive()) {//如果开启 45 | imm.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT,InputMethodManager.HIDE_NOT_ALWAYS);//关闭软键盘,开启方法相同,这个方法是切换开启与关闭状态的 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/base/BasePresenter.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.base; 2 | 3 | import com.pfh.app_mvp.network.ApiClient; 4 | 5 | import rx.Observable; 6 | import rx.Subscriber; 7 | import rx.Subscription; 8 | import rx.android.schedulers.AndroidSchedulers; 9 | import rx.schedulers.Schedulers; 10 | import rx.subscriptions.CompositeSubscription; 11 | 12 | /** 13 | * Created by afayp on 2017/3/6. 14 | */ 15 | 16 | public class BasePresenter implements IPresenter { 17 | 18 | public V mvpView; 19 | private CompositeSubscription mCompositeSubscription; 20 | protected ApiClient apiClient = ApiClient.getInstance(); 21 | 22 | @Override 23 | public void attachView(V view) { 24 | this.mvpView = view; 25 | } 26 | 27 | @Override 28 | public void detachView() { 29 | this.mvpView = null; 30 | onUnsubscribe(); 31 | } 32 | 33 | //RXjava取消注册,以避免内存泄露 34 | public void onUnsubscribe() { 35 | if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) { 36 | mCompositeSubscription.unsubscribe(); 37 | } 38 | } 39 | 40 | /** 41 | *P层的观察者和订阅者统一调用这个方法订阅,便于最后取消订阅 42 | */ 43 | public void addSubscription(Observable observable, Subscriber subscriber) { 44 | if (mCompositeSubscription == null) { 45 | mCompositeSubscription = new CompositeSubscription(); 46 | } 47 | mCompositeSubscription.add(observable 48 | .subscribeOn(Schedulers.io()) 49 | .observeOn(AndroidSchedulers.mainThread()) 50 | .subscribe(subscriber)); 51 | } 52 | 53 | public void addSubscription(Subscription subscription) { 54 | if (mCompositeSubscription == null) { 55 | mCompositeSubscription = new CompositeSubscription(); 56 | } 57 | mCompositeSubscription.add(subscription); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/base/IPresenter.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.base; 2 | 3 | /** 4 | * Created by afayp on 2017/3/6. 5 | */ 6 | 7 | public interface IPresenter { 8 | 9 | void attachView(V view); 10 | 11 | void detachView(); 12 | } 13 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/model/Repository.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | /** 9 | * model层,实现序列化接口Serializable或者Parcelable。 10 | * 并不会直接把该javabean对象set到xml中与view绑定,进行展示。 11 | * 通常是在viewmodel层中通过网络或者存储得到javabean, 12 | * 然后将Javabean中的字段与viewmodel中的属性进行一定的逻辑绑定, 13 | * 最后在xml中与view绑定的是viewmodel层对象。 14 | */ 15 | 16 | public class Repository implements Parcelable { 17 | public long id; 18 | public String name; 19 | public String description; 20 | public int forks; 21 | public int watchers; 22 | @SerializedName("stargazers_count") 23 | public int stars; 24 | public String language; 25 | public String homepage; 26 | public User owner; 27 | public boolean fork; 28 | 29 | public Repository() { 30 | } 31 | 32 | public boolean hasHomepage() { 33 | return homepage != null && !homepage.isEmpty(); 34 | } 35 | 36 | public boolean hasLanguage() { 37 | return language != null && !language.isEmpty(); 38 | } 39 | 40 | public boolean isFork() { 41 | return fork; 42 | } 43 | 44 | public long getId() { 45 | return id; 46 | } 47 | 48 | public void setId(long id) { 49 | this.id = id; 50 | } 51 | 52 | public String getName() { 53 | return name; 54 | } 55 | 56 | public void setName(String name) { 57 | this.name = name; 58 | } 59 | 60 | public String getDescription() { 61 | return description; 62 | } 63 | 64 | public void setDescription(String description) { 65 | this.description = description; 66 | } 67 | 68 | public int getForks() { 69 | return forks; 70 | } 71 | 72 | public void setForks(int forks) { 73 | this.forks = forks; 74 | } 75 | 76 | public int getWatchers() { 77 | return watchers; 78 | } 79 | 80 | public void setWatchers(int watchers) { 81 | this.watchers = watchers; 82 | } 83 | 84 | public int getStars() { 85 | return stars; 86 | } 87 | 88 | public void setStars(int stars) { 89 | this.stars = stars; 90 | } 91 | 92 | public String getLanguage() { 93 | return language; 94 | } 95 | 96 | public void setLanguage(String language) { 97 | this.language = language; 98 | } 99 | 100 | public String getHomepage() { 101 | return homepage; 102 | } 103 | 104 | public void setHomepage(String homepage) { 105 | this.homepage = homepage; 106 | } 107 | 108 | public User getOwner() { 109 | return owner; 110 | } 111 | 112 | public void setOwner(User owner) { 113 | this.owner = owner; 114 | } 115 | 116 | public void setFork(boolean fork) { 117 | this.fork = fork; 118 | } 119 | 120 | @Override 121 | public int describeContents() { 122 | return 0; 123 | } 124 | 125 | @Override 126 | public void writeToParcel(Parcel dest, int flags) { 127 | dest.writeLong(this.id); 128 | dest.writeString(this.name); 129 | dest.writeString(this.description); 130 | dest.writeInt(this.forks); 131 | dest.writeInt(this.watchers); 132 | dest.writeInt(this.stars); 133 | dest.writeString(this.language); 134 | dest.writeString(this.homepage); 135 | dest.writeParcelable(this.owner, 0); 136 | dest.writeByte(fork ? (byte) 1 : (byte) 0); 137 | } 138 | 139 | @Override 140 | public String toString() { 141 | return "Repository{" + 142 | "id=" + id + 143 | ", name='" + name + '\'' + 144 | ", description='" + description + '\'' + 145 | ", forks=" + forks + 146 | ", watchers=" + watchers + 147 | ", stars=" + stars + 148 | ", language='" + language + '\'' + 149 | ", homepage='" + homepage + '\'' + 150 | ", owner=" + owner + 151 | ", fork=" + fork + 152 | '}'; 153 | } 154 | 155 | protected Repository(Parcel in) { 156 | this.id = in.readLong(); 157 | this.name = in.readString(); 158 | this.description = in.readString(); 159 | this.forks = in.readInt(); 160 | this.watchers = in.readInt(); 161 | this.stars = in.readInt(); 162 | this.language = in.readString(); 163 | this.homepage = in.readString(); 164 | this.owner = in.readParcelable(User.class.getClassLoader()); 165 | this.fork = in.readByte() != 0; 166 | } 167 | 168 | public static final Creator CREATOR = new Creator() { 169 | public Repository createFromParcel(Parcel source) { 170 | return new Repository(source); 171 | } 172 | 173 | public Repository[] newArray(int size) { 174 | return new Repository[size]; 175 | } 176 | }; 177 | 178 | @Override 179 | public boolean equals(Object o) { 180 | if (this == o) return true; 181 | if (o == null || getClass() != o.getClass()) return false; 182 | 183 | Repository that = (Repository) o; 184 | 185 | if (id != that.id) return false; 186 | if (forks != that.forks) return false; 187 | if (watchers != that.watchers) return false; 188 | if (stars != that.stars) return false; 189 | if (fork != that.fork) return false; 190 | if (name != null ? !name.equals(that.name) : that.name != null) return false; 191 | if (description != null ? !description.equals(that.description) : that.description != null) 192 | return false; 193 | if (language != null ? !language.equals(that.language) : that.language != null) 194 | return false; 195 | if (homepage != null ? !homepage.equals(that.homepage) : that.homepage != null) 196 | return false; 197 | return !(owner != null ? !owner.equals(that.owner) : that.owner != null); 198 | 199 | } 200 | 201 | @Override 202 | public int hashCode() { 203 | int result = (int) (id ^ (id >>> 32)); 204 | result = 31 * result + (name != null ? name.hashCode() : 0); 205 | result = 31 * result + (description != null ? description.hashCode() : 0); 206 | result = 31 * result + forks; 207 | result = 31 * result + watchers; 208 | result = 31 * result + stars; 209 | result = 31 * result + (language != null ? language.hashCode() : 0); 210 | result = 31 * result + (homepage != null ? homepage.hashCode() : 0); 211 | result = 31 * result + (owner != null ? owner.hashCode() : 0); 212 | result = 31 * result + (fork ? 1 : 0); 213 | return result; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/model/User.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | public class User implements Parcelable { 9 | public long id; 10 | public String name; 11 | public String url; 12 | public String email; 13 | public String login; 14 | public String location; 15 | @SerializedName("avatar_url") 16 | public String avatarUrl; 17 | 18 | public User() { 19 | } 20 | 21 | public boolean hasEmail() { 22 | return email != null && !email.isEmpty(); 23 | } 24 | 25 | public boolean hasLocation() { 26 | return location != null && !location.isEmpty(); 27 | } 28 | 29 | @Override 30 | public int describeContents() { 31 | return 0; 32 | } 33 | 34 | @Override 35 | public void writeToParcel(Parcel dest, int flags) { 36 | dest.writeLong(this.id); 37 | dest.writeString(this.name); 38 | dest.writeString(this.url); 39 | dest.writeString(this.email); 40 | dest.writeString(this.login); 41 | dest.writeString(this.location); 42 | dest.writeString(this.avatarUrl); 43 | } 44 | 45 | protected User(Parcel in) { 46 | this.id = in.readLong(); 47 | this.name = in.readString(); 48 | this.url = in.readString(); 49 | this.email = in.readString(); 50 | this.login = in.readString(); 51 | this.location = in.readString(); 52 | this.avatarUrl = in.readString(); 53 | } 54 | 55 | public static final Creator CREATOR = new Creator() { 56 | public User createFromParcel(Parcel source) { 57 | return new User(source); 58 | } 59 | 60 | public User[] newArray(int size) { 61 | return new User[size]; 62 | } 63 | }; 64 | 65 | @Override 66 | public boolean equals(Object o) { 67 | if (this == o) return true; 68 | if (o == null || getClass() != o.getClass()) return false; 69 | 70 | User user = (User) o; 71 | 72 | if (id != user.id) return false; 73 | if (name != null ? !name.equals(user.name) : user.name != null) return false; 74 | if (url != null ? !url.equals(user.url) : user.url != null) return false; 75 | if (email != null ? !email.equals(user.email) : user.email != null) return false; 76 | if (login != null ? !login.equals(user.login) : user.login != null) return false; 77 | if (location != null ? !location.equals(user.location) : user.location != null) 78 | return false; 79 | return !(avatarUrl != null ? !avatarUrl.equals(user.avatarUrl) : user.avatarUrl != null); 80 | 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | int result = (int) (id ^ (id >>> 32)); 86 | result = 31 * result + (name != null ? name.hashCode() : 0); 87 | result = 31 * result + (url != null ? url.hashCode() : 0); 88 | result = 31 * result + (email != null ? email.hashCode() : 0); 89 | result = 31 * result + (login != null ? login.hashCode() : 0); 90 | result = 31 * result + (location != null ? location.hashCode() : 0); 91 | result = 31 * result + (avatarUrl != null ? avatarUrl.hashCode() : 0); 92 | return result; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/network/ApiClient.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.network; 2 | import com.pfh.app_mvp.model.Repository; 3 | import com.pfh.app_mvp.model.User; 4 | import java.util.List; 5 | import rx.Observable; 6 | import rx.Subscriber; 7 | import rx.Subscription; 8 | 9 | 10 | public class ApiClient extends RetrofitClient { 11 | 12 | private ApiClient(){ 13 | init(); 14 | } 15 | public static ApiClient getInstance(){ 16 | return SingletonHolder.INSTANCE; 17 | } 18 | 19 | private static class SingletonHolder{ 20 | private static final ApiClient INSTANCE = new ApiClient(); 21 | } 22 | 23 | //****所有具体的网络请求方法都要在下面注册,方便后期维护和测试、替换网络框架****// 24 | 25 | public Observable> publicRepositories(String username){ 26 | return apiService.publicRepositories(username) 27 | .compose(defaultSchedulers); 28 | } 29 | 30 | public Subscription userFromUrl(String userUrl, Subscriber subscriber){ 31 | return apiService.userFromUrl(userUrl) 32 | .compose(defaultSchedulers) 33 | .subscribe(subscriber); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/network/ApiService.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.network; 2 | 3 | 4 | import com.pfh.app_mvp.model.Repository; 5 | import com.pfh.app_mvp.model.User; 6 | import java.util.List; 7 | import retrofit2.http.GET; 8 | import retrofit2.http.Path; 9 | import retrofit2.http.Url; 10 | import rx.Observable; 11 | 12 | /** 13 | * 可以把所有api都放到一个接口下,如ApiStores 14 | * 也可以按照功能划分,多些几个接口,如GitHubService,XxxService, 15 | */ 16 | 17 | public interface ApiService { 18 | 19 | @GET("users/{username}/repos") 20 | Observable> publicRepositories(@Path("username") String username); 21 | 22 | @GET 23 | Observable userFromUrl(@Url String userUrl); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/network/RetrofitClient.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.network; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.pfh.app_mvp.BuildConfig; 7 | import com.pfh.app_mvp.MyApplication; 8 | import com.pfh.app_mvp.utils.NetUtil; 9 | import com.pfh.app_mvp.utils.SDCardUtil; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import okhttp3.Cache; 16 | import okhttp3.CacheControl; 17 | import okhttp3.ConnectionPool; 18 | import okhttp3.Interceptor; 19 | import okhttp3.OkHttpClient; 20 | import okhttp3.Request; 21 | import okhttp3.Response; 22 | import okhttp3.logging.HttpLoggingInterceptor; 23 | import retrofit2.Retrofit; 24 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 25 | import retrofit2.converter.gson.GsonConverterFactory; 26 | import rx.Observable; 27 | import rx.android.schedulers.AndroidSchedulers; 28 | import rx.schedulers.Schedulers; 29 | 30 | 31 | public class RetrofitClient { 32 | 33 | private static final String BASE_URL = "https://api.github.com/"; 34 | private static final int maxCacheSize = 5; // MB 35 | 36 | protected ApiService apiService; 37 | private static Retrofit retrofit; 38 | private static OkHttpClient okHttpClient; 39 | private static Context mContext; 40 | 41 | protected Observable.Transformer defaultSchedulers = new Observable.Transformer() { 42 | @Override 43 | public Object call(Object observable) { 44 | return ((Observable) observable).subscribeOn(Schedulers.io()) 45 | .unsubscribeOn(Schedulers.io()) 46 | .observeOn(AndroidSchedulers.mainThread()); 47 | } 48 | }; 49 | 50 | public ApiService getApiService(){ 51 | if (apiService == null){ 52 | apiService = retrofit.create(ApiService.class); 53 | } 54 | return apiService; 55 | } 56 | 57 | protected void init(){ 58 | this.mContext = MyApplication.getInstance(); 59 | initOkHttpClient(); 60 | initRetrofit(); 61 | if (apiService == null){ 62 | apiService = retrofit.create(ApiService.class); 63 | } 64 | } 65 | 66 | private void initRetrofit() { 67 | retrofit = new Retrofit.Builder() 68 | .baseUrl(BASE_URL) 69 | .client(okHttpClient) 70 | .addConverterFactory(GsonConverterFactory.create()) 71 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 72 | .build(); 73 | } 74 | 75 | private void initOkHttpClient(){ 76 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 77 | //打印请求log日志 78 | if (BuildConfig.DEBUG) { 79 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); 80 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 81 | builder.addInterceptor(loggingInterceptor); 82 | } 83 | //缓存 84 | File cacheFile = SDCardUtil.getCacheDir(mContext,"httpCache"); 85 | Cache cache = new Cache(cacheFile, 1024 * 1024 * maxCacheSize); 86 | Interceptor cacheInterceptor = new Interceptor() { 87 | @Override 88 | public Response intercept(Chain chain) throws IOException { 89 | Request request = chain.request(); 90 | if (!NetUtil.isConnected(mContext)){ 91 | request = request.newBuilder() 92 | .cacheControl(CacheControl.FORCE_CACHE) 93 | .build(); 94 | Log.d("OkHttp", "网络不可用请求拦截"); 95 | } 96 | Response response = chain.proceed(request); 97 | 98 | if (NetUtil.isConnected(mContext)){ 99 | int maxAge = 60; // 有网络 缓存60s 100 | response = response.newBuilder() 101 | //覆盖服务器响应头的Cache-Control,用我们自己的,因为服务器响应回来的可能不支持缓存 102 | .header("Cache-Control", "public,max-age="+maxAge) 103 | .removeHeader("Pragma") 104 | .build(); 105 | }else { 106 | // 无网络时,设置超时为4周 107 | int maxAge = 60 * 60 * 24 * 28; 108 | response.newBuilder() 109 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxAge) 110 | .removeHeader("Pragma") 111 | .build(); 112 | } 113 | return response; 114 | } 115 | }; 116 | builder.cache(cache).addInterceptor(cacheInterceptor); 117 | //设置超时 118 | builder.connectTimeout(15, TimeUnit.SECONDS); 119 | builder.readTimeout(20, TimeUnit.SECONDS); 120 | builder.writeTimeout(20, TimeUnit.SECONDS); 121 | //错误重连 122 | builder.retryOnConnectionFailure(true); 123 | builder.connectionPool(new ConnectionPool(8, 15, TimeUnit.SECONDS)); 124 | okHttpClient = builder.build(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/repos/ReposActivity.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.repos; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.widget.LinearLayoutManager; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.text.Editable; 8 | import android.text.TextWatcher; 9 | import android.view.View; 10 | import android.widget.EditText; 11 | import android.widget.ImageButton; 12 | import android.widget.ProgressBar; 13 | import android.widget.TextView; 14 | 15 | import com.pfh.app_mvp.R; 16 | import com.pfh.app_mvp.adapter.RepositoryAdapter; 17 | import com.pfh.app_mvp.base.BaseActivity; 18 | import com.pfh.app_mvp.model.Repository; 19 | import java.util.List; 20 | import butterknife.Bind; 21 | 22 | /** 23 | * view层的实现 24 | */ 25 | 26 | public class ReposActivity extends BaseActivity implements ReposContract.View{ 27 | 28 | @Bind(R.id.button_search) 29 | ImageButton button_search; 30 | @Bind(R.id.edit_text_username) 31 | EditText edit_text_username; 32 | @Bind(R.id.progress) 33 | ProgressBar progress; 34 | @Bind(R.id.text_info) 35 | TextView text_info; 36 | @Bind(R.id.repos_recycler_view) 37 | RecyclerView repos_recycler_view; 38 | 39 | @Override 40 | protected void onCreate(@Nullable Bundle savedInstanceState) { 41 | super.onCreate(savedInstanceState); 42 | setContentView(R.layout.activity_repos); 43 | 44 | edit_text_username.addTextChangedListener(new TextWatcher() { 45 | @Override 46 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 47 | 48 | } 49 | 50 | @Override 51 | public void onTextChanged(CharSequence s, int start, int before, int count) { 52 | button_search.setVisibility(s.toString().length() > 0 ? View.VISIBLE : View.GONE); 53 | } 54 | 55 | @Override 56 | public void afterTextChanged(Editable s) { 57 | 58 | } 59 | }); 60 | 61 | button_search.setOnClickListener(new View.OnClickListener() { 62 | @Override 63 | public void onClick(View v) { 64 | mPresenter.loadRepos(edit_text_username.getText().toString()); 65 | } 66 | }); 67 | setupRecyclerView(); 68 | } 69 | 70 | private void setupRecyclerView() { 71 | RepositoryAdapter adapter = new RepositoryAdapter(); 72 | repos_recycler_view.setAdapter(adapter); 73 | repos_recycler_view.setLayoutManager(new LinearLayoutManager(this)); 74 | } 75 | 76 | @Override 77 | protected ReposPresenter createPresenter() { 78 | return new ReposPresenter(this); 79 | } 80 | 81 | 82 | @Override 83 | public void showProgress() { 84 | progress.setVisibility(View.VISIBLE); 85 | } 86 | 87 | @Override 88 | public void hideProgress() { 89 | progress.setVisibility(View.GONE); 90 | } 91 | 92 | @Override 93 | public void showLoadErrorMsg(String errorInfo) { 94 | text_info.setVisibility(View.VISIBLE); 95 | text_info.setText(errorInfo); 96 | } 97 | 98 | @Override 99 | public void hideLoadErrorMsg() { 100 | text_info.setVisibility(View.GONE); 101 | } 102 | 103 | @Override 104 | public void showRepos(List repositories) { 105 | repos_recycler_view.setVisibility(View.VISIBLE); 106 | RepositoryAdapter adapter = (RepositoryAdapter) repos_recycler_view.getAdapter(); 107 | adapter.setRepositories(repositories); 108 | adapter.notifyDataSetChanged(); 109 | } 110 | 111 | @Override 112 | public void hideRepos() { 113 | repos_recycler_view.setVisibility(View.GONE); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/repos/ReposContract.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.repos; 2 | 3 | import com.pfh.app_mvp.model.Repository; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 契约类,包括 9 | * 1. view(activity)与Presenter交互的接口,定义界面改变需要的方法 10 | * - view要实现这个接口,然后通过p层的构造函数传入p层; 11 | * - P层持有view引用来改变view的状态,如showProgress,showData,hideProgress. 12 | * 2. Presenter的接口,定义了跟业务相关的控制逻辑(这个接口不是必须的,有些mvp的实现没有这个接口) 13 | * - p层要实现这个接口,然后通过BaseView的setPresenter传入view层 14 | * - view层要持有Presenter的引用,以便操作界面后去执行相关业务逻辑,比如点击按钮后去加载数据。 15 | * 一个简单的逻辑是: 点击view层的一个按钮,调用mPresenter.loadData()加载数据,加载完毕后在p层调用mView.showData()显示数据。 16 | */ 17 | 18 | public interface ReposContract { 19 | 20 | interface View { 21 | 22 | void showProgress(); 23 | 24 | void hideProgress(); 25 | 26 | void showLoadErrorMsg(String errorInfo); 27 | 28 | void hideLoadErrorMsg(); 29 | 30 | void showRepos(List repositories); 31 | 32 | void hideRepos(); 33 | 34 | } 35 | 36 | interface Presenter { 37 | 38 | void loadRepos(String username); 39 | 40 | void clickRepository(); 41 | 42 | } 43 | 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/repos/ReposPresenter.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.repos; 2 | import com.pfh.app_mvp.base.BasePresenter; 3 | import com.pfh.app_mvp.model.Repository; 4 | 5 | import java.util.List; 6 | 7 | import rx.Subscriber; 8 | 9 | 10 | public class ReposPresenter extends BasePresenter implements ReposContract.Presenter { 11 | 12 | public ReposPresenter(ReposContract.View tasksView) { 13 | attachView(tasksView); 14 | } 15 | 16 | @Override 17 | public void loadRepos(String username) { 18 | mvpView.showProgress(); 19 | mvpView.hideLoadErrorMsg(); 20 | mvpView.hideRepos(); 21 | 22 | apiClient.publicRepositories(username) 23 | .subscribe(new Subscriber>() { 24 | @Override 25 | public void onCompleted() { 26 | mvpView.hideLoadErrorMsg(); 27 | mvpView.hideProgress(); 28 | } 29 | 30 | @Override 31 | public void onError(Throwable e) { 32 | mvpView.hideProgress(); 33 | mvpView.hideRepos(); 34 | mvpView.showLoadErrorMsg(e.getMessage()); 35 | } 36 | 37 | @Override 38 | public void onNext(List repositories) { 39 | mvpView.showRepos(repositories); 40 | } 41 | }); 42 | 43 | } 44 | 45 | @Override 46 | public void clickRepository() { 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/utils/NetUtil.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.ComponentName; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.ConnectivityManager; 8 | import android.net.NetworkInfo; 9 | 10 | /** 11 | * 网络相关辅助类 12 | */ 13 | public class NetUtil { 14 | 15 | private NetUtil() { 16 | /* cannot be instantiated */ 17 | throw new UnsupportedOperationException("cannot be instantiated"); 18 | } 19 | 20 | /** 21 | * 判断网络是否连接 22 | * 23 | * @param context 24 | * @return 25 | */ 26 | public static boolean isConnected(Context context) { 27 | ConnectivityManager connectivityManager = (ConnectivityManager) context 28 | .getSystemService(Context.CONNECTIVITY_SERVICE); 29 | if (null != connectivityManager) { 30 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 31 | if (networkInfo != null){ 32 | return networkInfo.isAvailable(); 33 | } 34 | } 35 | return false; 36 | } 37 | 38 | /** 39 | * 判断是否是WIFI连接 40 | * 41 | * @param context 42 | * @return 43 | */ 44 | public static boolean isWIFI(Context context) { 45 | 46 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 47 | if (connectivityManager == null) 48 | return false; 49 | return connectivityManager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI; 50 | } 51 | 52 | /** 53 | * 打开网络设置界面 54 | */ 55 | public static void openSetting(Activity activity) { 56 | Intent intent = new Intent("/"); 57 | ComponentName cm = new ComponentName("com.android.settings", 58 | "com.android.settings.WirelessSettings"); 59 | intent.setComponent(cm); 60 | intent.setAction("android.intent.action.VIEW"); 61 | activity.startActivityForResult(intent, 0); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /app-mvp/src/main/java/com/pfh/app_mvp/utils/SDCardUtil.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp.utils; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.os.StatFs; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * SD卡辅助类 11 | */ 12 | public class SDCardUtil { 13 | 14 | private SDCardUtil() { 15 | /* cannot be instantiated */ 16 | throw new UnsupportedOperationException("cannot be instantiated"); 17 | } 18 | 19 | /** 20 | * 判断SDCard是否可用 21 | * 22 | * @return 23 | */ 24 | public static boolean isSDCardEnable() { 25 | return Environment.getExternalStorageState().equals( 26 | Environment.MEDIA_MOUNTED); 27 | } 28 | 29 | /** 30 | * 获取SD卡路径 31 | * 32 | * @return 33 | */ 34 | public static String getSDCardPath() { 35 | return Environment.getExternalStorageDirectory().getAbsolutePath() 36 | + File.separator; 37 | } 38 | 39 | /** 40 | * 获取SD卡的剩余容量 单位byte 41 | * 42 | * @return 43 | */ 44 | public static long getSDCardAllSize() { 45 | if (isSDCardEnable()) { 46 | StatFs stat = new StatFs(getSDCardPath()); 47 | // 获取空闲的数据块的数量 48 | long availableBlocks = (long) stat.getAvailableBlocks() - 4; 49 | // 获取单个数据块的大小(byte) 50 | long freeBlocks = stat.getAvailableBlocks(); 51 | return freeBlocks * availableBlocks; 52 | } 53 | return 0; 54 | } 55 | 56 | /** 57 | * 获取指定路径所在空间的剩余可用容量字节数,单位byte 58 | * 59 | * @param filePath 60 | * @return 容量字节 SDCard可用空间,内部存储可用空间 61 | */ 62 | public static long getFreeBytes(String filePath) 63 | { 64 | // 如果是sd卡的下的路径,则获取sd卡可用容量 65 | if (filePath.startsWith(getSDCardPath())) { 66 | filePath = getSDCardPath(); 67 | } else { 68 | // 如果是内部存储的路径,则获取内存存储的可用容量 69 | filePath = Environment.getDataDirectory().getAbsolutePath(); 70 | } 71 | StatFs stat = new StatFs(filePath); 72 | long availableBlocks = (long) stat.getAvailableBlocks() - 4; 73 | return stat.getBlockSize() * availableBlocks; 74 | } 75 | 76 | /** 77 | * 获取系统存储路径 78 | * 79 | * @return 80 | */ 81 | public static String getRootDirectoryPath() { 82 | return Environment.getRootDirectory().getAbsolutePath(); 83 | } 84 | 85 | 86 | /** 87 | * 获取缓存路径,存储临时文件,可被一键清理和卸载清理 88 | * 当SD卡存在或者SD卡不可被移除的时候, 89 | * 就调用getExternalCacheDir()方法来获取缓存路径, 90 | * 否则就调用getCacheDir()方法来获取缓存路径。 91 | * 前者获取到的就是/sdcard/Android/data//cache 这个路径, 92 | * 而后者获取到的是 /data/data//cache 这个路径。 93 | * @param context 94 | * @param uniqueName 95 | * @return 96 | */ 97 | public static File getCacheDir(Context context, String uniqueName) { 98 | String cachePath; 99 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 100 | || !Environment.isExternalStorageRemovable()) { 101 | cachePath = context.getExternalCacheDir().getPath(); 102 | } else { 103 | cachePath = context.getCacheDir().getPath(); 104 | } 105 | return new File(cachePath + File.separator + uniqueName); 106 | } 107 | 108 | /*返回缓存路径*/ 109 | public static File getCacheDir(Context context) { 110 | String cachePath; 111 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 112 | || !Environment.isExternalStorageRemovable()) { 113 | cachePath = context.getExternalCacheDir().getPath(); 114 | } else { 115 | cachePath = context.getCacheDir().getPath(); 116 | } 117 | return new File(cachePath); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /app-mvp/src/main/res/layout/activity_repos.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 29 | 30 | 39 | 40 | 50 | 51 | 52 | 60 | 61 | 74 | 75 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /app-mvp/src/main/res/layout/item_repo.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 20 | 21 | 32 | 33 | 44 | 45 | 49 | 50 | 54 | 55 | 63 | 64 | 72 | 73 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /app-mvp/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp/src/main/res/mipmap-xhdpi/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp/src/main/res/mipmap-xhdpi/ic_search_white_36dp.png -------------------------------------------------------------------------------- /app-mvp/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvp/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvp/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app-mvp/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | #000000 8 | #ffffff 9 | #75ffffff 10 | #e1e1e1 11 | #3F51B5 12 | #303F9F 13 | #C5CAE9 14 | #03A9F4 15 | #212121 16 | #727272 17 | #FFFFFF 18 | #cbcbcb 19 | 20 | -------------------------------------------------------------------------------- /app-mvp/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 12dp 6 | 12dp 7 | 6dp 8 | 6dp 9 | 10 | -------------------------------------------------------------------------------- /app-mvp/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | app-mvp 3 | enter your name 4 | 5 | 6 | Settings 7 | %d \nStars 8 | %d \nWatchers 9 | %d \nForks 10 | Oops, something went wrong 11 | This account doesn\'t have any public repository 12 | Oops, Octocat doesn\'t know that username 13 | GitHub username 14 | Enter a GitHub username above to see its repositories 15 | This repository is a fork 16 | Language: %s 17 | 18 | -------------------------------------------------------------------------------- /app-mvp/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app-mvp/src/test/java/com/pfh/app_mvp/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvp; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app-mvvm/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app-mvvm/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion rootProject.ext.compileSdkVersion 5 | buildToolsVersion rootProject.ext.buildToolsVersion 6 | 7 | defaultConfig { 8 | applicationId "com.pfh.app_mvvm" 9 | minSdkVersion rootProject.ext.minSdkVersion 10 | targetSdkVersion rootProject.ext.targetSdkVersion 11 | versionCode rootProject.ext.versionCode 12 | versionName rootProject.ext.versionName 13 | 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | dataBinding { 18 | enabled = true 19 | } 20 | 21 | buildTypes { 22 | release { 23 | minifyEnabled false 24 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 25 | } 26 | } 27 | buildToolsVersion '25.0.0' 28 | } 29 | 30 | dependencies { 31 | compile fileTree(dir: 'libs', include: ['*.jar']) 32 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 33 | exclude group: 'com.android.support', module: 'support-annotations' 34 | }) 35 | 36 | Map dependencies = rootProject.ext.dependencies; 37 | 38 | compile dependencies.appCompat 39 | compile dependencies.cardView 40 | compile dependencies.recyclerView 41 | compile dependencies.retrofit 42 | compile dependencies.retrofitConverterGson 43 | compile dependencies.retrofitAdapterRxJava 44 | compile dependencies.okhttp 45 | compile dependencies.okhttpLoggingInterceptor 46 | compile dependencies.picasso 47 | compile dependencies.rxAndroid 48 | compile dependencies.circleImageView 49 | 50 | testCompile dependencies.jUnit 51 | testCompile dependencies.mockito 52 | testCompile dependencies.robolectric 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /app-mvvm/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 F:\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-mvvm/src/androidTest/java/com/pfh/app_mvvm/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.pfh.app_mvvm", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app-mvvm/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | 6 | import rx.Scheduler; 7 | import rx.schedulers.Schedulers; 8 | 9 | 10 | public class MyApplication extends Application { 11 | 12 | private Scheduler defaultSubscribeScheduler;// todo 什么用? 13 | 14 | private static MyApplication instance; 15 | 16 | public static MyApplication getInstance() { 17 | return instance; 18 | } 19 | 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | instance = this; 24 | } 25 | 26 | /** 27 | * todo ? 28 | * @param context 29 | * @return 30 | */ 31 | public static MyApplication get(Context context){ 32 | return (MyApplication) context.getApplicationContext(); 33 | } 34 | 35 | // public ApiService getApiService(){ 36 | // if (apiService == null){ 37 | // apiService = RetrofitClient.getApiService(); 38 | // } 39 | // return apiService; 40 | // } 41 | // 42 | // //For setting mocks during testing 43 | // public void setApiService(ApiService apiService) { 44 | // this.apiService = apiService; 45 | // } 46 | 47 | public Scheduler defaultSubscribeScheduler() { 48 | if (defaultSubscribeScheduler == null) { 49 | defaultSubscribeScheduler = Schedulers.io(); 50 | } 51 | return defaultSubscribeScheduler; 52 | } 53 | 54 | //User to change scheduler from tests 55 | public void setDefaultSubscribeScheduler(Scheduler scheduler) { 56 | this.defaultSubscribeScheduler = scheduler; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/adapter/RepositoryAdapter.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.adapter; 2 | 3 | import android.databinding.DataBindingUtil; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.ViewGroup; 7 | 8 | import com.pfh.app_mvvm.R; 9 | import com.pfh.app_mvvm.databinding.ItemRepoBinding; 10 | import com.pfh.app_mvvm.model.Repository; 11 | import com.pfh.app_mvvm.viewmodel.ItemRepoViewModel; 12 | 13 | import java.util.Collections; 14 | import java.util.List; 15 | 16 | /** 17 | * Created by Administrator on 2016/12/3. 18 | */ 19 | 20 | public class RepositoryAdapter extends RecyclerView.Adapter { 21 | 22 | private List repositories; 23 | 24 | public RepositoryAdapter() { 25 | this.repositories = Collections.emptyList(); 26 | } 27 | 28 | public RepositoryAdapter(List repositories) { 29 | this.repositories = repositories; 30 | } 31 | 32 | public void setRepositories(List repositories) { 33 | this.repositories = repositories; 34 | } 35 | 36 | 37 | @Override 38 | public RepositoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 39 | ItemRepoBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), 40 | R.layout.item_repo, 41 | parent,false); 42 | return new RepositoryViewHolder(binding); 43 | } 44 | 45 | @Override 46 | public void onBindViewHolder(RepositoryViewHolder holder, int position) { 47 | holder.bindRepository(repositories.get(position)); 48 | } 49 | 50 | @Override 51 | public int getItemCount() { 52 | return repositories.size(); 53 | } 54 | 55 | public static class RepositoryViewHolder extends RecyclerView.ViewHolder{ 56 | ItemRepoBinding binding; 57 | 58 | public RepositoryViewHolder(ItemRepoBinding binding) { 59 | super(binding.cardView);// binding.getRoot() 60 | this.binding = binding; 61 | } 62 | 63 | void bindRepository(Repository repository){ 64 | if (binding.getViewModel() == null){ 65 | binding.setViewModel(new ItemRepoViewModel(itemView.getContext(),repository)); 66 | }else { 67 | binding.getViewModel().setRepository(repository); 68 | } 69 | 70 | } 71 | 72 | public void setBinding(ItemRepoBinding binding){ 73 | this.binding = binding; 74 | } 75 | 76 | public ItemRepoBinding getBinding(){ 77 | return binding; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/model/Repository.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | import com.google.gson.annotations.SerializedName; 6 | 7 | /** 8 | * model层,实现序列化接口Serializable或者Parcelable。 9 | * 并不会直接把该javabean对象set到xml中与view绑定,进行展示。 10 | * 通常是在viewmodel层中通过网络或者存储得到javabean, 11 | * 然后将Javabean中的字段与viewmodel中的属性进行一定的逻辑绑定, 12 | * 最后在xml中与view绑定的是viewmodel层对象。 13 | */ 14 | 15 | public class Repository implements Parcelable { 16 | public long id; 17 | public String name; 18 | public String description; 19 | public int forks; 20 | public int watchers; 21 | @SerializedName("stargazers_count") 22 | public int stars; 23 | public String language; 24 | public String homepage; 25 | public User owner; 26 | public boolean fork; 27 | 28 | public Repository() { 29 | } 30 | 31 | public boolean hasHomepage() { 32 | return homepage != null && !homepage.isEmpty(); 33 | } 34 | 35 | public boolean hasLanguage() { 36 | return language != null && !language.isEmpty(); 37 | } 38 | 39 | public boolean isFork() { 40 | return fork; 41 | } 42 | 43 | @Override 44 | public int describeContents() { 45 | return 0; 46 | } 47 | 48 | @Override 49 | public void writeToParcel(Parcel dest, int flags) { 50 | dest.writeLong(this.id); 51 | dest.writeString(this.name); 52 | dest.writeString(this.description); 53 | dest.writeInt(this.forks); 54 | dest.writeInt(this.watchers); 55 | dest.writeInt(this.stars); 56 | dest.writeString(this.language); 57 | dest.writeString(this.homepage); 58 | dest.writeParcelable(this.owner, 0); 59 | dest.writeByte(fork ? (byte) 1 : (byte) 0); 60 | } 61 | 62 | protected Repository(Parcel in) { 63 | this.id = in.readLong(); 64 | this.name = in.readString(); 65 | this.description = in.readString(); 66 | this.forks = in.readInt(); 67 | this.watchers = in.readInt(); 68 | this.stars = in.readInt(); 69 | this.language = in.readString(); 70 | this.homepage = in.readString(); 71 | this.owner = in.readParcelable(User.class.getClassLoader()); 72 | this.fork = in.readByte() != 0; 73 | } 74 | 75 | public static final Creator CREATOR = new Creator() { 76 | public Repository createFromParcel(Parcel source) { 77 | return new Repository(source); 78 | } 79 | 80 | public Repository[] newArray(int size) { 81 | return new Repository[size]; 82 | } 83 | }; 84 | 85 | @Override 86 | public boolean equals(Object o) { 87 | if (this == o) return true; 88 | if (o == null || getClass() != o.getClass()) return false; 89 | 90 | Repository that = (Repository) o; 91 | 92 | if (id != that.id) return false; 93 | if (forks != that.forks) return false; 94 | if (watchers != that.watchers) return false; 95 | if (stars != that.stars) return false; 96 | if (fork != that.fork) return false; 97 | if (name != null ? !name.equals(that.name) : that.name != null) return false; 98 | if (description != null ? !description.equals(that.description) : that.description != null) 99 | return false; 100 | if (language != null ? !language.equals(that.language) : that.language != null) 101 | return false; 102 | if (homepage != null ? !homepage.equals(that.homepage) : that.homepage != null) 103 | return false; 104 | return !(owner != null ? !owner.equals(that.owner) : that.owner != null); 105 | 106 | } 107 | 108 | @Override 109 | public int hashCode() { 110 | int result = (int) (id ^ (id >>> 32)); 111 | result = 31 * result + (name != null ? name.hashCode() : 0); 112 | result = 31 * result + (description != null ? description.hashCode() : 0); 113 | result = 31 * result + forks; 114 | result = 31 * result + watchers; 115 | result = 31 * result + stars; 116 | result = 31 * result + (language != null ? language.hashCode() : 0); 117 | result = 31 * result + (homepage != null ? homepage.hashCode() : 0); 118 | result = 31 * result + (owner != null ? owner.hashCode() : 0); 119 | result = 31 * result + (fork ? 1 : 0); 120 | return result; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/model/User.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.model; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.gson.annotations.SerializedName; 7 | 8 | public class User implements Parcelable { 9 | public long id; 10 | public String name; 11 | public String url; 12 | public String email; 13 | public String login; 14 | public String location; 15 | @SerializedName("avatar_url") 16 | public String avatarUrl; 17 | 18 | public User() { 19 | } 20 | 21 | public boolean hasEmail() { 22 | return email != null && !email.isEmpty(); 23 | } 24 | 25 | public boolean hasLocation() { 26 | return location != null && !location.isEmpty(); 27 | } 28 | 29 | @Override 30 | public int describeContents() { 31 | return 0; 32 | } 33 | 34 | @Override 35 | public void writeToParcel(Parcel dest, int flags) { 36 | dest.writeLong(this.id); 37 | dest.writeString(this.name); 38 | dest.writeString(this.url); 39 | dest.writeString(this.email); 40 | dest.writeString(this.login); 41 | dest.writeString(this.location); 42 | dest.writeString(this.avatarUrl); 43 | } 44 | 45 | protected User(Parcel in) { 46 | this.id = in.readLong(); 47 | this.name = in.readString(); 48 | this.url = in.readString(); 49 | this.email = in.readString(); 50 | this.login = in.readString(); 51 | this.location = in.readString(); 52 | this.avatarUrl = in.readString(); 53 | } 54 | 55 | public static final Creator CREATOR = new Creator() { 56 | public User createFromParcel(Parcel source) { 57 | return new User(source); 58 | } 59 | 60 | public User[] newArray(int size) { 61 | return new User[size]; 62 | } 63 | }; 64 | 65 | @Override 66 | public boolean equals(Object o) { 67 | if (this == o) return true; 68 | if (o == null || getClass() != o.getClass()) return false; 69 | 70 | User user = (User) o; 71 | 72 | if (id != user.id) return false; 73 | if (name != null ? !name.equals(user.name) : user.name != null) return false; 74 | if (url != null ? !url.equals(user.url) : user.url != null) return false; 75 | if (email != null ? !email.equals(user.email) : user.email != null) return false; 76 | if (login != null ? !login.equals(user.login) : user.login != null) return false; 77 | if (location != null ? !location.equals(user.location) : user.location != null) 78 | return false; 79 | return !(avatarUrl != null ? !avatarUrl.equals(user.avatarUrl) : user.avatarUrl != null); 80 | 81 | } 82 | 83 | @Override 84 | public int hashCode() { 85 | int result = (int) (id ^ (id >>> 32)); 86 | result = 31 * result + (name != null ? name.hashCode() : 0); 87 | result = 31 * result + (url != null ? url.hashCode() : 0); 88 | result = 31 * result + (email != null ? email.hashCode() : 0); 89 | result = 31 * result + (login != null ? login.hashCode() : 0); 90 | result = 31 * result + (location != null ? location.hashCode() : 0); 91 | result = 31 * result + (avatarUrl != null ? avatarUrl.hashCode() : 0); 92 | return result; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/network/ApiClient.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.network; 2 | 3 | import com.pfh.app_mvvm.model.Repository; 4 | import com.pfh.app_mvvm.model.User; 5 | 6 | import java.util.List; 7 | 8 | import rx.Observable; 9 | import rx.Subscriber; 10 | import rx.Subscription; 11 | 12 | /** 13 | * Created by Administrator on 2016/12/5. 14 | */ 15 | 16 | public class ApiClient extends RetrofitClient { 17 | 18 | private ApiClient(){ 19 | init(); 20 | } 21 | public static ApiClient getInstance(){ 22 | return SingletonHolder.INSTANCE; 23 | } 24 | 25 | private static class SingletonHolder{ 26 | private static final ApiClient INSTANCE = new ApiClient(); 27 | } 28 | 29 | //****所有具体的网络请求方法都要在下面注册,方便后期维护和测试、替换网络框架****// 30 | //****统一前面写参数,最后写subscriber****// 31 | 32 | // public Subscription publicRepositories(String username, HttpSubscriber> subscriber){ 33 | // return apiService.test(username) 34 | // .compose(defaultSchedulers) 35 | // .subscribe(subscriber); 36 | // } 37 | 38 | public Observable> publicRepositories(String username){ 39 | return apiService.publicRepositories(username) 40 | .compose(defaultSchedulers); 41 | } 42 | 43 | public Subscription publicRepositories(String username, Subscriber> subscriber){ 44 | return apiService.publicRepositories(username) 45 | .compose(defaultSchedulers) 46 | .subscribe(subscriber); 47 | } 48 | 49 | public Subscription userFromUrl(String userUrl, Subscriber subscriber){ 50 | return apiService.userFromUrl(userUrl) 51 | .compose(defaultSchedulers) 52 | .subscribe(subscriber); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/network/ApiService.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.network; 2 | 3 | import com.pfh.app_mvvm.model.Repository; 4 | import com.pfh.app_mvvm.model.User; 5 | 6 | import java.util.List; 7 | 8 | import retrofit2.http.GET; 9 | import retrofit2.http.Path; 10 | import retrofit2.http.Url; 11 | import rx.Observable; 12 | 13 | /** 14 | * 可以把所有api都放到一个接口下,如ApiStores 15 | * 可以按照功能划分,多些几个接口,如GithubService,XxxService, 16 | */ 17 | 18 | public interface ApiService { 19 | 20 | @GET("users/{username}/repos") 21 | Observable> publicRepositories(@Path("username") String username); 22 | 23 | @GET 24 | Observable userFromUrl(@Url String userUrl); 25 | 26 | // 如果对返回结果做过统一处理,可以采用这种方式 27 | @GET("users/{username}/repos") 28 | Observable>> test(@Path("username") String username); 29 | 30 | } 31 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/network/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.network; 2 | 3 | import android.net.ParseException; 4 | 5 | import com.google.gson.JsonParseException; 6 | 7 | import org.json.JSONException; 8 | 9 | import java.net.ConnectException; 10 | 11 | import retrofit2.adapter.rxjava.HttpException; 12 | 13 | /** 14 | * 这个类对错误统一处理,将错误信息转换成对用户较友好的方式 15 | * 根据需求对不同错误可以统一处理 16 | */ 17 | 18 | public class ExceptionHandler { 19 | 20 | //对应HTTP的状态码 21 | private static final int UNAUTHORIZED = 401; 22 | private static final int FORBIDDEN = 403; 23 | private static final int NOT_FOUND = 404; 24 | private static final int REQUEST_TIMEOUT = 408; 25 | private static final int INTERNAL_SERVER_ERROR = 500; 26 | private static final int BAD_GATEWAY = 502; 27 | private static final int SERVICE_UNAVAILABLE = 503; 28 | private static final int GATEWAY_TIMEOUT = 504; 29 | 30 | public static ResponeThrowable handleException(Throwable e) { 31 | ResponeThrowable ex; 32 | if (e instanceof HttpException) { 33 | HttpException httpException = (HttpException) e; //HTTP错误 34 | ex = new ResponeThrowable(e, ERROR.HTTP_ERROR); 35 | switch (httpException.code()) { 36 | case UNAUTHORIZED: 37 | case FORBIDDEN: 38 | case NOT_FOUND: 39 | case REQUEST_TIMEOUT: 40 | case GATEWAY_TIMEOUT: 41 | case INTERNAL_SERVER_ERROR: 42 | case BAD_GATEWAY: 43 | case SERVICE_UNAVAILABLE: 44 | default: 45 | ex.message = "网络错误"; 46 | break; 47 | } 48 | return ex; 49 | } else if (e instanceof ServerException) { 50 | ServerException resultException = (ServerException) e; //服务器返回的错误 51 | ex = new ResponeThrowable(resultException, resultException.code); 52 | ex.message = resultException.message; 53 | return ex; 54 | } else if (e instanceof JsonParseException 55 | || e instanceof JSONException 56 | || e instanceof ParseException) { 57 | ex = new ResponeThrowable(e, ERROR.PARSE_ERROR); //均视为解析错误 58 | ex.message = "解析错误"; 59 | return ex; 60 | } else if (e instanceof ConnectException) { 61 | ex = new ResponeThrowable(e, ERROR.NETWORD_ERROR); //均视为网络错误 62 | ex.message = "连接失败"; 63 | return ex; 64 | } else if (e instanceof javax.net.ssl.SSLHandshakeException) { 65 | ex = new ResponeThrowable(e, ERROR.SSL_ERROR); 66 | ex.message = "证书验证失败"; 67 | return ex; 68 | } 69 | else { 70 | ex = new ResponeThrowable(e, ERROR.UNKNOWN); //未知错误 71 | ex.message = "未知错误"; 72 | return ex; 73 | } 74 | } 75 | 76 | /** 77 | * 约定异常 78 | */ 79 | class ERROR { 80 | /** 81 | * 未知错误 82 | */ 83 | public static final int UNKNOWN = 1000; 84 | /** 85 | * 解析错误 86 | */ 87 | public static final int PARSE_ERROR = 1001; 88 | /** 89 | * 网络错误 90 | */ 91 | public static final int NETWORD_ERROR = 1002; 92 | /** 93 | * 协议出错 94 | */ 95 | public static final int HTTP_ERROR = 1003; 96 | 97 | /** 98 | * 证书出错 99 | */ 100 | public static final int SSL_ERROR = 1005; 101 | } 102 | 103 | public static class ResponeThrowable extends Exception { 104 | public int code; 105 | public String message; 106 | 107 | public ResponeThrowable(Throwable throwable, int code) { 108 | super(throwable); 109 | this.code = code; 110 | } 111 | 112 | // 用于已知错误类型 113 | public ResponeThrowable(String msg, int code) { 114 | super(); 115 | this.message = msg; 116 | this.code = code; 117 | } 118 | } 119 | 120 | public class ServerException extends RuntimeException { 121 | public int code; 122 | public String message; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/network/HttpResult.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.network; 2 | 3 | import com.pfh.app_mvvm.utils.Constant; 4 | 5 | /**todo 6 | * Http请求结果,返回的结构统一为 7 | * { "code": 0, "msg": "成功", "data": {} } 8 | * code== 0代表success 不等于0则表示error,其中 data里面数据如果是列表则是 JSONArray,非列表则是JSONObject。 9 | * 在code!= 0的时候,抛出个自定义的ApiException,进入到subscriber的onError中,在onError中处理错误信息。 10 | * code不为0表示请求失败(不包括网络原因,主要是业务逻辑出错,如某请求字段为空),msg中为具体原因,最好是能直接给用户的友好提示。 11 | * 这样在onNext中首先过滤掉由于业务逻辑出错的请求失败,直接走到onFailure中. 12 | */ 13 | 14 | public class HttpResult { 15 | private int code; 16 | private String msg; 17 | private T data; 18 | 19 | public int getCode() { 20 | return code; 21 | } 22 | 23 | public void setCode(int code) { 24 | this.code = code; 25 | } 26 | 27 | public String getMsg() { 28 | return msg; 29 | } 30 | 31 | public void setMsg(String msg) { 32 | this.msg = msg; 33 | } 34 | 35 | public T getData() { 36 | return data; 37 | } 38 | 39 | public void setData(T data) { 40 | this.data = data; 41 | } 42 | 43 | public boolean isSuccess(){ 44 | return code == Constant.HTTP_SUCCESS; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/network/HttpSubscriber.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.network; 2 | 3 | import android.util.Log; 4 | import android.widget.Toast; 5 | 6 | import com.pfh.app_mvvm.MyApplication; 7 | import com.pfh.app_mvvm.utils.NetUtil; 8 | 9 | import rx.Subscriber; 10 | 11 | public abstract class HttpSubscriber extends Subscriber> { 12 | 13 | private boolean isShowProgressDialog = true;//是否显示加载框,默认显示 14 | 15 | 16 | public void setShowProgressDialog(boolean isShow){ 17 | isShowProgressDialog = isShow; 18 | } 19 | 20 | @Override 21 | public void onCompleted() { 22 | onFinished(); 23 | if (isShowProgressDialog){ 24 | dismissProgress(); 25 | } 26 | } 27 | 28 | @Override 29 | public void onError(Throwable e) { 30 | Log.e("TAG",e.toString()); 31 | if (e instanceof ExceptionHandler.ResponeThrowable){//如果是Exception 32 | onFailure(ExceptionHandler.handleException(e)); 33 | }else { 34 | onFailure(new ExceptionHandler.ResponeThrowable(e, ExceptionHandler.ERROR.UNKNOWN)); 35 | } 36 | if (isShowProgressDialog){ 37 | dismissProgress(); 38 | } 39 | } 40 | 41 | @Override 42 | public void onNext(HttpResult result) { 43 | if (result.isSuccess()){ 44 | onSuccess(result.getData()); 45 | }else { 46 | onFailure(new ExceptionHandler.ResponeThrowable(result.getMsg(),result.getCode())); 47 | } 48 | } 49 | 50 | @Override 51 | public void onStart() { 52 | super.onStart(); 53 | if (!NetUtil.isConnected(MyApplication.getInstance())){ //每次都先判断网络状态,是否必要看需求 54 | Toast.makeText(MyApplication.getInstance(), "当前网络不可用,请检查网络情况", Toast.LENGTH_SHORT).show(); 55 | onCompleted(); 56 | return; 57 | } 58 | if (isShowProgressDialog){ 59 | showProgress();//显示Dialog在onStart里不太合适吧,它会在Subscriber的subscribeOn线程执行,应该用doOnSubscribe() todo 60 | } 61 | } 62 | 63 | 64 | public void showProgress(){ 65 | 66 | } 67 | 68 | public void dismissProgress(){ 69 | 70 | } 71 | 72 | public abstract void onSuccess(T t); 73 | 74 | public abstract void onFailure(ExceptionHandler.ResponeThrowable responeThrowable); 75 | 76 | public abstract void onFinished(); 77 | 78 | 79 | } 80 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/network/RetrofitClient.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.network; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import com.pfh.app_mvvm.BuildConfig; 7 | import com.pfh.app_mvvm.MyApplication; 8 | import com.pfh.app_mvvm.utils.NetUtil; 9 | import com.pfh.app_mvvm.utils.SDCardUtil; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.concurrent.TimeUnit; 14 | 15 | import okhttp3.Cache; 16 | import okhttp3.CacheControl; 17 | import okhttp3.ConnectionPool; 18 | import okhttp3.Interceptor; 19 | import okhttp3.OkHttpClient; 20 | import okhttp3.Request; 21 | import okhttp3.Response; 22 | import okhttp3.logging.HttpLoggingInterceptor; 23 | import retrofit2.Retrofit; 24 | import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; 25 | import retrofit2.converter.gson.GsonConverterFactory; 26 | import rx.Observable; 27 | import rx.android.schedulers.AndroidSchedulers; 28 | import rx.schedulers.Schedulers; 29 | 30 | /** 31 | * Created by Administrator on 2016/12/3. 32 | */ 33 | 34 | public class RetrofitClient { 35 | 36 | private static final String BASE_URL = "https://api.github.com/"; 37 | private static final int DEFAULT_TIMEOUT = 10; 38 | private static final int maxCacheSize = 5; // MB 39 | 40 | protected ApiService apiService; 41 | private static Retrofit retrofit; 42 | private static OkHttpClient okHttpClient; 43 | private static Context mContext; // 防止内存泄露,用了application的context,不知道合不合适? 还是使用WeakReference? 44 | 45 | protected Observable.Transformer defaultSchedulers = new Observable.Transformer() { 46 | @Override 47 | public Object call(Object observable) { 48 | return ((Observable) observable).subscribeOn(Schedulers.io()) 49 | .unsubscribeOn(Schedulers.io()) 50 | .observeOn(AndroidSchedulers.mainThread()); 51 | } 52 | }; 53 | // Observable.Transformer schedulersTransformer() { 54 | // return new Observable.Transformer() { 55 | // @Override 56 | // public Object call(Object observable) { 57 | // return ((Observable) observable).subscribeOn(Schedulers.io()) 58 | // .unsubscribeOn(Schedulers.io()) 59 | // .observeOn(AndroidSchedulers.mainThread()); 60 | // } 61 | // }; 62 | // } 63 | 64 | public ApiService getApiService(){ 65 | if (apiService == null){ 66 | apiService = retrofit.create(ApiService.class); 67 | } 68 | return apiService; 69 | } 70 | 71 | protected void init(){ 72 | this.mContext = MyApplication.getInstance(); 73 | initOkHttpClient(); 74 | initRetrofit(); 75 | if (apiService == null){ 76 | apiService = retrofit.create(ApiService.class); 77 | } 78 | } 79 | 80 | private void initRetrofit() { 81 | retrofit = new Retrofit.Builder() 82 | .baseUrl(BASE_URL) 83 | .client(okHttpClient) 84 | .addConverterFactory(GsonConverterFactory.create()) 85 | .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) 86 | .build(); 87 | } 88 | 89 | private void initOkHttpClient(){ 90 | OkHttpClient.Builder builder = new OkHttpClient.Builder(); 91 | //打印请求log日志 92 | if (BuildConfig.DEBUG) { 93 | HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); 94 | loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 95 | builder.addInterceptor(loggingInterceptor); 96 | } 97 | //缓存 98 | File cacheFile = SDCardUtil.getCacheDir(mContext,"httpCache"); 99 | Cache cache = new Cache(cacheFile, 1024 * 1024 * maxCacheSize); 100 | Interceptor cacheInterceptor = new Interceptor() { 101 | @Override 102 | public Response intercept(Chain chain) throws IOException { 103 | Request request = chain.request(); 104 | if (!NetUtil.isConnected(mContext)){ 105 | request = request.newBuilder() 106 | .cacheControl(CacheControl.FORCE_CACHE) 107 | .build(); 108 | Log.d("OkHttp", "网络不可用请求拦截"); 109 | } 110 | Response response = chain.proceed(request); 111 | 112 | if (NetUtil.isConnected(mContext)){ 113 | int maxAge = 60; // 有网络 缓存60s 114 | response = response.newBuilder() 115 | //覆盖服务器响应头的Cache-Control,用我们自己的,因为服务器响应回来的可能不支持缓存 116 | .header("Cache-Control", "public,max-age="+maxAge) 117 | .removeHeader("Pragma") 118 | .build(); 119 | }else { 120 | // 无网络时,设置超时为4周 121 | int maxAge = 60 * 60 * 24 * 28; 122 | response.newBuilder() 123 | .header("Cache-Control", "public, only-if-cached, max-stale=" + maxAge) 124 | .removeHeader("Pragma") 125 | .build(); 126 | } 127 | return response; 128 | } 129 | }; 130 | builder.cache(cache).addInterceptor(cacheInterceptor); 131 | //设置超时 132 | builder.connectTimeout(15, TimeUnit.SECONDS); 133 | builder.readTimeout(20, TimeUnit.SECONDS); 134 | builder.writeTimeout(20, TimeUnit.SECONDS); 135 | //错误重连 136 | builder.retryOnConnectionFailure(true); 137 | builder.connectionPool(new ConnectionPool(8, 15, TimeUnit.SECONDS)); 138 | okHttpClient = builder.build(); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/utils/Constant.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.utils; 2 | 3 | /** 4 | * 存放一些常量 5 | */ 6 | 7 | public class Constant { 8 | 9 | public static final int HTTP_SUCCESS = 0; 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/utils/NetUtil.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.utils; 2 | 3 | import android.app.Activity; 4 | import android.content.ComponentName; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.net.ConnectivityManager; 8 | import android.net.NetworkInfo; 9 | 10 | /** 11 | * 网络相关辅助类 12 | */ 13 | public class NetUtil { 14 | 15 | private NetUtil() { 16 | /* cannot be instantiated */ 17 | throw new UnsupportedOperationException("cannot be instantiated"); 18 | } 19 | 20 | /** 21 | * 判断网络是否连接 22 | * 23 | * @param context 24 | * @return 25 | */ 26 | public static boolean isConnected(Context context) { 27 | ConnectivityManager connectivityManager = (ConnectivityManager) context 28 | .getSystemService(Context.CONNECTIVITY_SERVICE); 29 | if (null != connectivityManager) { 30 | NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); 31 | if (networkInfo != null){ 32 | return networkInfo.isAvailable(); 33 | } 34 | } 35 | return false; 36 | } 37 | 38 | /** 39 | * 判断是否是WIFI连接 40 | * 41 | * @param context 42 | * @return 43 | */ 44 | public static boolean isWIFI(Context context) { 45 | 46 | ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 47 | if (connectivityManager == null) 48 | return false; 49 | return connectivityManager.getActiveNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI; 50 | } 51 | 52 | /** 53 | * 打开网络设置界面 54 | */ 55 | public static void openSetting(Activity activity) { 56 | Intent intent = new Intent("/"); 57 | ComponentName cm = new ComponentName("com.android.settings", 58 | "com.android.settings.WirelessSettings"); 59 | intent.setComponent(cm); 60 | intent.setAction("android.intent.action.VIEW"); 61 | activity.startActivityForResult(intent, 0); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/utils/SDCardUtil.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.utils; 2 | 3 | import android.content.Context; 4 | import android.os.Environment; 5 | import android.os.StatFs; 6 | 7 | import java.io.File; 8 | 9 | /** 10 | * SD卡辅助类 11 | */ 12 | public class SDCardUtil { 13 | 14 | private SDCardUtil() { 15 | /* cannot be instantiated */ 16 | throw new UnsupportedOperationException("cannot be instantiated"); 17 | } 18 | 19 | /** 20 | * 判断SDCard是否可用 21 | * 22 | * @return 23 | */ 24 | public static boolean isSDCardEnable() { 25 | return Environment.getExternalStorageState().equals( 26 | Environment.MEDIA_MOUNTED); 27 | } 28 | 29 | /** 30 | * 获取SD卡路径 31 | * 32 | * @return 33 | */ 34 | public static String getSDCardPath() { 35 | return Environment.getExternalStorageDirectory().getAbsolutePath() 36 | + File.separator; 37 | } 38 | 39 | /** 40 | * 获取SD卡的剩余容量 单位byte 41 | * 42 | * @return 43 | */ 44 | public static long getSDCardAllSize() { 45 | if (isSDCardEnable()) { 46 | StatFs stat = new StatFs(getSDCardPath()); 47 | // 获取空闲的数据块的数量 48 | long availableBlocks = (long) stat.getAvailableBlocks() - 4; 49 | // 获取单个数据块的大小(byte) 50 | long freeBlocks = stat.getAvailableBlocks(); 51 | return freeBlocks * availableBlocks; 52 | } 53 | return 0; 54 | } 55 | 56 | /** 57 | * 获取指定路径所在空间的剩余可用容量字节数,单位byte 58 | * 59 | * @param filePath 60 | * @return 容量字节 SDCard可用空间,内部存储可用空间 61 | */ 62 | public static long getFreeBytes(String filePath) 63 | { 64 | // 如果是sd卡的下的路径,则获取sd卡可用容量 65 | if (filePath.startsWith(getSDCardPath())) { 66 | filePath = getSDCardPath(); 67 | } else { 68 | // 如果是内部存储的路径,则获取内存存储的可用容量 69 | filePath = Environment.getDataDirectory().getAbsolutePath(); 70 | } 71 | StatFs stat = new StatFs(filePath); 72 | long availableBlocks = (long) stat.getAvailableBlocks() - 4; 73 | return stat.getBlockSize() * availableBlocks; 74 | } 75 | 76 | /** 77 | * 获取系统存储路径 78 | * 79 | * @return 80 | */ 81 | public static String getRootDirectoryPath() { 82 | return Environment.getRootDirectory().getAbsolutePath(); 83 | } 84 | 85 | 86 | /** 87 | * 获取缓存路径,存储临时文件,可被一键清理和卸载清理 88 | * 当SD卡存在或者SD卡不可被移除的时候, 89 | * 就调用getExternalCacheDir()方法来获取缓存路径, 90 | * 否则就调用getCacheDir()方法来获取缓存路径。 91 | * 前者获取到的就是/sdcard/Android/data//cache 这个路径, 92 | * 而后者获取到的是 /data/data//cache 这个路径。 93 | * @param context 94 | * @param uniqueName 95 | * @return 96 | */ 97 | public static File getCacheDir(Context context, String uniqueName) { 98 | String cachePath; 99 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 100 | || !Environment.isExternalStorageRemovable()) { 101 | cachePath = context.getExternalCacheDir().getPath(); 102 | } else { 103 | cachePath = context.getCacheDir().getPath(); 104 | } 105 | return new File(cachePath + File.separator + uniqueName); 106 | } 107 | 108 | /*返回缓存路径*/ 109 | public static File getCacheDir(Context context) { 110 | String cachePath; 111 | if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) 112 | || !Environment.isExternalStorageRemovable()) { 113 | cachePath = context.getExternalCacheDir().getPath(); 114 | } else { 115 | cachePath = context.getCacheDir().getPath(); 116 | } 117 | return new File(cachePath); 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/view/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.view; 2 | 3 | import android.content.Context; 4 | import android.databinding.DataBindingUtil; 5 | import android.os.Bundle; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.view.inputmethod.InputMethodManager; 10 | 11 | import com.pfh.app_mvvm.R; 12 | import com.pfh.app_mvvm.adapter.RepositoryAdapter; 13 | import com.pfh.app_mvvm.databinding.ActivityMainBinding; 14 | import com.pfh.app_mvvm.model.Repository; 15 | import com.pfh.app_mvvm.viewmodel.MainViewModel; 16 | 17 | import java.util.List; 18 | 19 | public class MainActivity extends AppCompatActivity implements MainViewModel.DataListener { 20 | 21 | private ActivityMainBinding binding; 22 | private MainViewModel mainViewModel; 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | binding = DataBindingUtil.setContentView(this, R.layout.activity_main); 28 | mainViewModel = new MainViewModel(this, this); 29 | binding.setViewModel(mainViewModel); 30 | setSupportActionBar(binding.toolbar); 31 | setupRecyclerView(binding.reposRecyclerView); 32 | } 33 | 34 | @Override 35 | protected void onDestroy() { 36 | super.onDestroy(); 37 | mainViewModel.destory(); 38 | } 39 | 40 | private void setupRecyclerView(RecyclerView recyclerView) { 41 | RepositoryAdapter adapter = new RepositoryAdapter(); 42 | recyclerView.setAdapter(adapter); 43 | recyclerView.setLayoutManager(new LinearLayoutManager(this)); 44 | } 45 | @Override 46 | public void onRepositoriesChanged(List repositories) { 47 | RepositoryAdapter adapter = (RepositoryAdapter) binding.reposRecyclerView.getAdapter(); 48 | adapter.setRepositories(repositories); 49 | adapter.notifyDataSetChanged(); 50 | hideSoftKeyboard(); 51 | } 52 | 53 | private void hideSoftKeyboard() { 54 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 55 | imm.hideSoftInputFromWindow(binding.editTextUsername.getWindowToken(), 0); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/view/RepositoryActivity.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.view; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.databinding.DataBindingUtil; 6 | import android.os.Bundle; 7 | import android.support.annotation.Nullable; 8 | import android.support.v7.app.ActionBar; 9 | import android.support.v7.app.AppCompatActivity; 10 | 11 | import com.pfh.app_mvvm.R; 12 | import com.pfh.app_mvvm.databinding.ActivityRepositoryBinding; 13 | import com.pfh.app_mvvm.model.Repository; 14 | import com.pfh.app_mvvm.viewmodel.RepositoryViewModel; 15 | 16 | /** 17 | * Created by Administrator on 2016/12/3. 18 | */ 19 | 20 | public class RepositoryActivity extends AppCompatActivity { 21 | private static final String EXTRA_REPOSITORY = "EXTRA_REPOSITORY"; 22 | private ActivityRepositoryBinding binding; 23 | private RepositoryViewModel repositoryViewModel; 24 | 25 | // 注意这种intent启动方式 26 | public static Intent newIntent(Context context, Repository repository) { 27 | Intent intent = new Intent(context, RepositoryActivity.class); 28 | intent.putExtra(EXTRA_REPOSITORY, repository); 29 | return intent; 30 | } 31 | 32 | @Override 33 | protected void onCreate(@Nullable Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | binding = DataBindingUtil.setContentView(this, R.layout.activity_repository); 36 | setSupportActionBar(binding.toolbar); 37 | ActionBar actionBar = getSupportActionBar(); 38 | if (actionBar != null) { 39 | actionBar.setDisplayHomeAsUpEnabled(true); 40 | } 41 | 42 | Repository repository = getIntent().getParcelableExtra(EXTRA_REPOSITORY); 43 | repositoryViewModel = new RepositoryViewModel(this, repository); 44 | binding.setViewModel(repositoryViewModel); 45 | 46 | //Currently there is no way of setting an activity title using data binding 47 | setTitle(repository.name); 48 | } 49 | 50 | @Override 51 | protected void onDestroy() { 52 | super.onDestroy(); 53 | repositoryViewModel.destory(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/viewmodel/BaseViewModel.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.viewmodel; 2 | 3 | import android.content.Context; 4 | 5 | import rx.Subscription; 6 | 7 | /** 8 | * BaseViewModel抽取出viewmodel类的一些共性 9 | */ 10 | 11 | public class BaseViewModel implements IViewModel{ 12 | 13 | protected Context mContext; 14 | protected Subscription subscription; 15 | 16 | 17 | @Override 18 | public void destory() { 19 | if (subscription != null && !subscription.isUnsubscribed()) subscription.unsubscribe(); 20 | subscription = null; 21 | mContext = null; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/viewmodel/IViewModel.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.viewmodel; 2 | 3 | /** 4 | * Created by Administrator on 2016/12/3. 5 | */ 6 | 7 | public interface IViewModel { 8 | void destory(); 9 | } 10 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/viewmodel/ItemRepoViewModel.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.viewmodel; 2 | 3 | import android.content.Context; 4 | import android.databinding.BaseObservable; 5 | import android.view.View; 6 | 7 | import com.pfh.app_mvvm.R; 8 | import com.pfh.app_mvvm.model.Repository; 9 | import com.pfh.app_mvvm.view.RepositoryActivity; 10 | 11 | /** 12 | * item的viewmodel,为什么采用继承BaseObservable的形式 13 | */ 14 | 15 | public class ItemRepoViewModel extends BaseObservable implements IViewModel { 16 | 17 | private Repository repository; 18 | private Context context; 19 | 20 | public ItemRepoViewModel(Context context, Repository repository) { 21 | this.repository = repository; 22 | this.context = context; 23 | } 24 | 25 | public String getName() { 26 | return repository.name; 27 | } 28 | 29 | public String getDescription() { 30 | return repository.description; 31 | } 32 | 33 | public String getStars() { 34 | return context.getString(R.string.text_stars, repository.stars); 35 | } 36 | 37 | public String getWatchers() { 38 | return context.getString(R.string.text_watchers, repository.watchers); 39 | } 40 | 41 | public String getForks() { 42 | return context.getString(R.string.text_forks, repository.forks); 43 | } 44 | 45 | public void onItemClick(View view) { 46 | context.startActivity(RepositoryActivity.newIntent(context, repository)); 47 | } 48 | 49 | // Allows recycling ItemRepoViewModels within the recyclerview adapter 50 | public void setRepository(Repository repository) { 51 | this.repository = repository; 52 | notifyChange();// todo ? 53 | } 54 | 55 | @Override 56 | public void destory() { 57 | //In this case destroy doesn't need to do anything because there is not async calls 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/viewmodel/MainViewModel.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.viewmodel; 2 | 3 | import android.content.Context; 4 | import android.databinding.ObservableField; 5 | import android.databinding.ObservableInt; 6 | import android.text.Editable; 7 | import android.text.TextWatcher; 8 | import android.util.Log; 9 | import android.view.KeyEvent; 10 | import android.view.View; 11 | import android.view.inputmethod.EditorInfo; 12 | import android.widget.TextView; 13 | 14 | import com.pfh.app_mvvm.R; 15 | import com.pfh.app_mvvm.model.Repository; 16 | import com.pfh.app_mvvm.network.ApiClient; 17 | 18 | import java.util.List; 19 | 20 | import retrofit2.adapter.rxjava.HttpException; 21 | import rx.Subscriber; 22 | 23 | /** 24 | * viewmodel 不持有任何控件,持有context,model 25 | */ 26 | 27 | public class MainViewModel extends BaseViewModel { 28 | 29 | private static final String TAG = "MainViewModel"; 30 | 31 | public ObservableInt infoMessageVisibility; 32 | public ObservableInt progressVisibility; 33 | public ObservableInt recyclerViewVisibility; 34 | public ObservableInt searchButtonVisibility; 35 | public ObservableField infoMessage; 36 | 37 | private List repositories;//数据 38 | private DataListener dataListener; 39 | public String editTextUsernameValue; 40 | 41 | public MainViewModel(Context context, DataListener dataListener){ 42 | this.mContext = context; 43 | this.dataListener = dataListener; 44 | infoMessageVisibility = new ObservableInt(View.VISIBLE); 45 | progressVisibility = new ObservableInt(View.INVISIBLE); 46 | recyclerViewVisibility = new ObservableInt(View.INVISIBLE); 47 | searchButtonVisibility = new ObservableInt(View.GONE); 48 | infoMessage = new ObservableField<>(context.getString(R.string.default_info_message)); 49 | } 50 | 51 | public interface DataListener { 52 | void onRepositoriesChanged(List repositories); 53 | } 54 | 55 | public void setDataListener(DataListener dataListener) { 56 | this.dataListener = dataListener; 57 | } 58 | 59 | public void onClickSearch(View view){ 60 | loadGithubRepos(editTextUsernameValue); 61 | } 62 | 63 | public boolean onSearchAction(TextView view, int actionId, KeyEvent event){ 64 | if (actionId == EditorInfo.IME_ACTION_SEARCH){ 65 | String username = view.getText().toString(); 66 | if (username.length() > 0) loadGithubRepos(username); 67 | return true; 68 | } 69 | return false; 70 | } 71 | 72 | public TextWatcher getUsernameEditTextWatcher(){ 73 | return new TextWatcher() { 74 | @Override 75 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 76 | 77 | } 78 | 79 | @Override 80 | public void onTextChanged(CharSequence s, int start, int before, int count) { 81 | editTextUsernameValue = s.toString(); 82 | searchButtonVisibility.set(s.length() > 0 ? View.VISIBLE : View.GONE); 83 | } 84 | 85 | @Override 86 | public void afterTextChanged(Editable s) { 87 | 88 | } 89 | }; 90 | } 91 | 92 | private void loadGithubRepos(String username) { 93 | progressVisibility.set(View.VISIBLE); 94 | recyclerViewVisibility.set(View.INVISIBLE); 95 | infoMessageVisibility.set(View.INVISIBLE); 96 | 97 | // //如果先前一个未注销,则先注销 Todo ? 每个都这么写一遍太麻烦。。 98 | // if (subscription != null && !subscription.isUnsubscribed()){ 99 | // subscription.unsubscribe(); 100 | // } 101 | 102 | this.subscription = ApiClient.getInstance().publicRepositories(username, new Subscriber>() { 103 | @Override 104 | public void onCompleted() { 105 | if (dataListener != null) dataListener.onRepositoriesChanged(repositories); 106 | progressVisibility.set(View.INVISIBLE); 107 | if (!repositories.isEmpty()) { 108 | recyclerViewVisibility.set(View.VISIBLE); 109 | } else { 110 | infoMessage.set(mContext.getString(R.string.text_empty_repos)); 111 | infoMessageVisibility.set(View.VISIBLE); 112 | } 113 | } 114 | 115 | @Override 116 | public void onError(Throwable e) { 117 | Log.e(TAG, "Error loading GitHub repos "+ e.getMessage()); 118 | progressVisibility.set(View.INVISIBLE); 119 | infoMessage.set(mContext.getString(R.string.error_username_not_found)); 120 | infoMessageVisibility.set(View.VISIBLE); 121 | } 122 | 123 | @Override 124 | public void onNext(List repositories) { 125 | Log.e(TAG, "Repos loaded " + repositories); 126 | MainViewModel.this.repositories = repositories; 127 | } 128 | }); 129 | 130 | } 131 | 132 | @Override 133 | public void destory() { 134 | super.destory(); 135 | dataListener = null; 136 | } 137 | 138 | private static boolean isHttp404(Throwable error) { 139 | return error instanceof HttpException && ((HttpException) error).code() == 404; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app-mvvm/src/main/java/com/pfh/app_mvvm/viewmodel/RepositoryViewModel.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm.viewmodel; 2 | 3 | import android.content.Context; 4 | import android.databinding.BindingAdapter; 5 | import android.databinding.ObservableField; 6 | import android.databinding.ObservableInt; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.widget.ImageView; 10 | 11 | import com.pfh.app_mvvm.R; 12 | import com.pfh.app_mvvm.model.Repository; 13 | import com.pfh.app_mvvm.model.User; 14 | import com.pfh.app_mvvm.network.ApiClient; 15 | import com.squareup.picasso.Picasso; 16 | 17 | import rx.Subscriber; 18 | 19 | /** 20 | * Created by Administrator on 2016/12/3. 21 | */ 22 | 23 | public class RepositoryViewModel extends BaseViewModel { 24 | 25 | private static final String TAG = "RepositoryViewModel"; 26 | 27 | private Repository repository; 28 | 29 | public ObservableField ownerName; 30 | public ObservableField ownerEmail; 31 | public ObservableField ownerLocation; 32 | public ObservableInt ownerEmailVisibility; 33 | public ObservableInt ownerLocationVisibility; 34 | public ObservableInt ownerLayoutVisibility; 35 | 36 | public RepositoryViewModel(Context context, final Repository repository) { 37 | this.repository = repository; 38 | this.mContext = context; 39 | this.ownerName = new ObservableField<>(); 40 | this.ownerEmail = new ObservableField<>(); 41 | this.ownerLocation = new ObservableField<>(); 42 | this.ownerLayoutVisibility = new ObservableInt(View.INVISIBLE); 43 | this.ownerEmailVisibility = new ObservableInt(View.VISIBLE); 44 | this.ownerLocationVisibility = new ObservableInt(View.VISIBLE); 45 | // Trigger loading the rest of the user data as soon as the view model is created. 46 | // It's odd having to trigger this from here. Cases where accessing to the data model 47 | // needs to happen because of a change in the Activity/Fragment lifecycle 48 | // (i.e. an activity created) don't work very well with this MVVM pattern. 49 | // It also makes this class more difficult to test. Hopefully a better solution will be found 50 | loadFullUser(repository.owner.url); 51 | } 52 | 53 | //*********各种get方法 用来给xml中的变量提供数据 54 | public String getDescription() { 55 | return repository.description; 56 | } 57 | 58 | public String getHomepage() { 59 | return repository.homepage; 60 | } 61 | 62 | public int getHomepageVisibility() { 63 | return repository.hasHomepage() ? View.VISIBLE : View.GONE; 64 | } 65 | 66 | public String getLanguage() { 67 | return mContext.getString(R.string.text_language, repository.language); 68 | } 69 | 70 | public int getLanguageVisibility() { 71 | return repository.hasLanguage() ? View.VISIBLE : View.GONE; 72 | } 73 | 74 | public int getForkVisibility() { 75 | return repository.isFork() ? View.VISIBLE : View.GONE; 76 | } 77 | 78 | public String getOwnerAvatarUrl() { 79 | return repository.owner.avatarUrl; 80 | } 81 | 82 | 83 | @BindingAdapter({"imageUrl"}) 84 | public static void loadImage(ImageView view, String imageUrl) { 85 | Picasso.with(view.getContext()) 86 | .load(imageUrl) 87 | .placeholder(R.drawable.placeholder) 88 | .into(view); 89 | } 90 | 91 | private void loadFullUser(String url) { 92 | 93 | ApiClient.getInstance().userFromUrl(url, new Subscriber() { 94 | @Override 95 | public void onCompleted() { 96 | 97 | } 98 | 99 | @Override 100 | public void onError(Throwable e) { 101 | 102 | } 103 | 104 | @Override 105 | public void onNext(User user) { 106 | Log.i(TAG, "Full user data loaded " + user); 107 | ownerName.set(user.name); 108 | ownerEmail.set(user.email); 109 | ownerLocation.set(user.location); 110 | ownerEmailVisibility.set(user.hasEmail() ? View.VISIBLE : View.GONE); 111 | ownerLocationVisibility.set(user.hasLocation() ? View.VISIBLE : View.GONE); 112 | ownerLayoutVisibility.set(View.VISIBLE); 113 | } 114 | }); 115 | 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /app-mvvm/src/main/res/drawable-xhdpi/ic_search_white_36dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvvm/src/main/res/drawable-xhdpi/ic_search_white_36dp.png -------------------------------------------------------------------------------- /app-mvvm/src/main/res/drawable/octocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvvm/src/main/res/drawable/octocat.png -------------------------------------------------------------------------------- /app-mvvm/src/main/res/drawable/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvvm/src/main/res/drawable/placeholder.png -------------------------------------------------------------------------------- /app-mvvm/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 11 | 12 | 13 | 17 | 18 | 26 | 27 | 38 | 39 | 50 | 51 | 64 | 65 | 66 | 67 | 68 | 76 | 77 | 91 | 92 | 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /app-mvvm/src/main/res/layout/item_repo.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 11 | 12 | 13 | 22 | 23 | 30 | 31 | 43 | 44 | 56 | 57 | 61 | 62 | 66 | 67 | 76 | 77 | 86 | 87 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /app-mvvm/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvvm/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvvm/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvvm/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvvm/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvvm/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvvm/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvvm/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvvm/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app-mvvm/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app-mvvm/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app-mvvm/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #000000 4 | #ffffff 5 | #75ffffff 6 | #e1e1e1 7 | #3F51B5 8 | #303F9F 9 | #C5CAE9 10 | #03A9F4 11 | #212121 12 | #727272 13 | #FFFFFF 14 | #cbcbcb 15 | 16 | -------------------------------------------------------------------------------- /app-mvvm/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 12dp 6 | 12dp 7 | 6dp 8 | 6dp 9 | 10 | -------------------------------------------------------------------------------- /app-mvvm/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | App-mvvm 3 | enter your name 4 | 5 | 6 | Settings 7 | %d \nStars 8 | %d \nWatchers 9 | %d \nForks 10 | Oops, something went wrong 11 | This account doesn\'t have any public repository 12 | Oops, Octocat doesn\'t know that username 13 | GitHub username 14 | Enter a GitHub username above to see its repositories 15 | This repository is a fork 16 | Language: %s 17 | 18 | -------------------------------------------------------------------------------- /app-mvvm/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 16 | 17 | 18 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app-mvvm/src/test/java/com/pfh/app_mvvm/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.pfh.app_mvvm; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion '25.0.0' 6 | defaultConfig { 7 | applicationId "com.pfh.architecture" 8 | minSdkVersion 15 9 | targetSdkVersion 24 10 | versionCode 1 11 | versionName "1.0" 12 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 25 | exclude group: 'com.android.support', module: 'support-annotations' 26 | }) 27 | compile 'com.android.support:appcompat-v7:24.2.1' 28 | testCompile 'junit:junit:4.12' 29 | } 30 | -------------------------------------------------------------------------------- /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 F:\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/androidTest/java/com/pfh/architecture/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.pfh.architecture; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.pfh.architecture", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/pfh/architecture/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.pfh.architecture; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | 6 | public class MainActivity extends AppCompatActivity { 7 | 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | setContentView(R.layout.activity_main); 12 | 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Architecture 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/pfh/architecture/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.pfh.architecture; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | apply from: 'config.gradle' 3 | 4 | buildscript { 5 | repositories { 6 | jcenter() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:2.3.1' 10 | //classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | jcenter() 20 | } 21 | } 22 | 23 | task clean(type: Delete) { 24 | delete rootProject.buildDir 25 | } 26 | 27 | -------------------------------------------------------------------------------- /config.gradle: -------------------------------------------------------------------------------- 1 | ext { 2 | compileSdkVersion = 24 3 | buildToolsVersion = '24.0.0' 4 | minSdkVersion = 15 5 | targetSdkVersion = 24 6 | versionCode = 1 7 | versionName = '1.0' 8 | supportLibraryVersion = '24.2.1' 9 | retrofitVersion = '2.1.0' 10 | okhttpVersion = '3.4.2' 11 | 12 | dependencies = [ 13 | appCompat: "com.android.support:appcompat-v7:$supportLibraryVersion", 14 | supportDesign: "com.android.support:design:$supportLibraryVersion", 15 | cardView: "com.android.support:cardview-v7:$supportLibraryVersion", 16 | recyclerView: "com.android.support:recyclerview-v7:$supportLibraryVersion", 17 | jUnit: 'junit:junit:4.12', 18 | mockito: 'org.mockito:mockito-core:1.10.19', // https://github.com/mockito/mockito 19 | robolectric: 'org.robolectric:robolectric:3.1.4', // https://github.com/robolectric/robolectric 20 | // https://github.com/square/retrofit 21 | retrofit: "com.squareup.retrofit2:retrofit:$retrofitVersion", 22 | retrofitConverterGson: "com.squareup.retrofit2:converter-gson:$retrofitVersion", 23 | retrofitAdapterRxJava: "com.squareup.retrofit2:adapter-rxjava:$retrofitVersion", 24 | okhttp: "com.squareup.okhttp3:okhttp:$okhttpVersion", // https://github.com/square/okhttp 25 | okhttpLoggingInterceptor: "com.squareup.okhttp3:logging-interceptor:$okhttpVersion", 26 | rxJava: 'io.reactivex:rxjava:1.1.6', // https://github.com/ReactiveX/RxJava 27 | rxAndroid: 'io.reactivex:rxandroid:1.2.1', // https://github.com/ReactiveX/RxAndroid 28 | eventBus: "org.greenrobot:eventbus:3.0.0", // https://github.com/greenrobot/EventBus 29 | glide: 'com.github.bumptech.glide:glide:3.7.0', // https://github.com/bumptech/glide 30 | picasso: 'com.squareup.picasso:picasso:2.5.2', 31 | circleImageView: 'de.hdodenhof:circleimageview:1.3.0', 32 | looger : "com.orhanobut:logger:1.15", // https://github.com/orhanobut/logger, 33 | // https://github.com/square/leakcanary 34 | leakcanary: "com.squareup.leakcanary:leakcanary-android:1.5", 35 | leakcanaryNoop: "com.squareup.leakcanary:leakcanary-android-no-op:1.5", 36 | butterknife: "com.jakewharton:butterknife:7.0.1" 37 | ] 38 | } -------------------------------------------------------------------------------- /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 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/afayp/Architecture/5573565e3fdbbea6b1db4d493e77a226ea12a5dd/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Jun 05 11:20:19 CST 2017 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-3.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':app-mvvm', ':app-mvp', ':app-mvp-dagger' 2 | --------------------------------------------------------------------------------