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 |
--------------------------------------------------------------------------------