diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index 6c66bb8..03e304f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -24,6 +24,14 @@ dependencies {
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
- compile 'com.android.support:appcompat-v7:24.0.0-beta1'
+ compile 'com.android.support:appcompat-v7:24.2.1'
+ compile 'com.android.support:recyclerview-v7:24.2.1'
+ compile 'com.google.code.gson:gson:2.4'
+ compile 'com.squareup.retrofit2:retrofit:2.1.0'
+ compile 'com.squareup.retrofit2:converter-gson:2.1.0'
+ compile 'com.jakewharton.rxbinding:rxbinding:0.4.0'
testCompile 'junit:junit:4.12'
+
+ compile 'io.reactivex:rxandroid:1.2.1'
+ compile 'io.reactivex:rxjava:1.2.0'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a8ef6a3..2145ebf 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,13 +2,15 @@
+
+
-
+
diff --git a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/MainActivity.java b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/MainActivity.java
deleted file mode 100644
index 5a7913e..0000000
--- a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/MainActivity.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.ensarsarajcic.reactivegithubsample;
-
-import android.support.v7.app.AppCompatActivity;
-import android.os.Bundle;
-
-public class MainActivity extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- }
-}
diff --git a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubRepo.java b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubRepo.java
new file mode 100644
index 0000000..2b6296e
--- /dev/null
+++ b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubRepo.java
@@ -0,0 +1,20 @@
+package com.ensarsarajcic.reactivegithubsample.models;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Created by ensar on 03/10/16.
+ */
+public class GitHubRepo {
+ public static final String TAG = GitHubRepo.class.getSimpleName();
+
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubSearchResponse.java b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubSearchResponse.java
new file mode 100644
index 0000000..1a19d03
--- /dev/null
+++ b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubSearchResponse.java
@@ -0,0 +1,21 @@
+package com.ensarsarajcic.reactivegithubsample.models;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+/**
+ * Created by ensar on 03/10/16.
+ */
+public class GitHubSearchResponse {
+
+ private List items;
+
+ public List getItems() {
+ return items;
+ }
+
+ public void setItems(List items) {
+ this.items = items;
+ }
+}
diff --git a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubUser.java b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubUser.java
new file mode 100644
index 0000000..d9d35e4
--- /dev/null
+++ b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/models/GitHubUser.java
@@ -0,0 +1,38 @@
+package com.ensarsarajcic.reactivegithubsample.models;
+
+import com.google.gson.annotations.SerializedName;
+
+/**
+ * Created by ensar on 03/10/16.
+ */
+public class GitHubUser {
+ public static final String TAG = GitHubUser.class.getSimpleName();
+
+ private String login;
+ private String html_url;
+ private String avatar_url;
+
+ public String getLogin() {
+ return login;
+ }
+
+ public void setLogin(String login) {
+ this.login = login;
+ }
+
+ public String getHtml_url() {
+ return html_url;
+ }
+
+ public void setHtml_url(String html_url) {
+ this.html_url = html_url;
+ }
+
+ public String getAvatar_url() {
+ return avatar_url;
+ }
+
+ public void setAvatar_url(String avatar_url) {
+ this.avatar_url = avatar_url;
+ }
+}
diff --git a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/network/GitHubApi.java b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/network/GitHubApi.java
new file mode 100644
index 0000000..58eef95
--- /dev/null
+++ b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/network/GitHubApi.java
@@ -0,0 +1,27 @@
+package com.ensarsarajcic.reactivegithubsample.network;
+
+import com.ensarsarajcic.reactivegithubsample.models.GitHubRepo;
+import com.ensarsarajcic.reactivegithubsample.models.GitHubSearchResponse;
+import com.ensarsarajcic.reactivegithubsample.models.GitHubUser;
+
+import java.util.List;
+
+import retrofit2.Call;
+import retrofit2.http.GET;
+import retrofit2.http.Path;
+import retrofit2.http.Query;
+
+/**
+ * Created by ensar on 03/10/16.
+ */
+public interface GitHubApi {
+
+ @GET("/users")
+ Call> getUsers(@Query("since") Integer since);
+
+ @GET("/search/users")
+ Call searchForUsers(@Query("q") String query);
+
+ @GET("/users/{user}/repos")
+ Call> getUserRepos(@Path("user") String user);
+}
diff --git a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/network/RestClient.java b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/network/RestClient.java
new file mode 100644
index 0000000..2b56725
--- /dev/null
+++ b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/network/RestClient.java
@@ -0,0 +1,34 @@
+package com.ensarsarajcic.reactivegithubsample.network;
+
+import retrofit2.Retrofit;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+/**
+ * Created by ensar on 03/10/16.
+ */
+public class RestClient {
+ public static final String TAG = RestClient.class.getSimpleName();
+
+ private static GitHubApi gitHubApi;
+ private static Retrofit restAdapter = null;
+
+ public static Retrofit getRestAdapter() {
+ if(restAdapter == null) {
+ Retrofit.Builder builder = new Retrofit.Builder()
+ .addConverterFactory(GsonConverterFactory.create())
+ .baseUrl("https://api.github.com/");
+ restAdapter = builder.build();
+ }
+ return restAdapter;
+ }
+
+
+ public static GitHubApi getGitHubApi() {
+ if(gitHubApi == null) {
+ gitHubApi = getRestAdapter().create(GitHubApi.class);
+ }
+ return gitHubApi;
+ }
+
+
+}
diff --git a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/views/GitHubUsersAdapter.java b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/views/GitHubUsersAdapter.java
new file mode 100644
index 0000000..7dc9e30
--- /dev/null
+++ b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/views/GitHubUsersAdapter.java
@@ -0,0 +1,222 @@
+package com.ensarsarajcic.reactivegithubsample.views;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.ensarsarajcic.reactivegithubsample.R;
+import com.ensarsarajcic.reactivegithubsample.models.GitHubRepo;
+import com.ensarsarajcic.reactivegithubsample.models.GitHubUser;
+import com.ensarsarajcic.reactivegithubsample.network.RestClient;
+import com.jakewharton.rxbinding.view.RxView;
+import com.jakewharton.rxbinding.widget.RxCompoundButton;
+import com.jakewharton.rxbinding.widget.RxTextView;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import rx.Observable;
+import rx.Subscriber;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.functions.Func1;
+import rx.functions.Func2;
+import rx.schedulers.Schedulers;
+import rx.subscriptions.CompositeSubscription;
+
+
+/**
+ * Created by ensar on 03/10/16.
+ */
+public class GitHubUsersAdapter extends RecyclerView.Adapter {
+ public static final String TAG = GitHubUsersAdapter.class.getSimpleName();
+
+ private List users;
+ private CompositeSubscription compositeSubscription;
+
+ public GitHubUsersAdapter(List users) {
+ this.users = users;
+ compositeSubscription = new CompositeSubscription();
+ }
+
+ public void setItems(List users) {
+ this.users = users;
+ }
+
+ @Override
+ public GitHubUserViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View itemView = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.list_item, parent, false);
+
+ return new GitHubUserViewHolder(itemView);
+ }
+
+ @Override
+ public void onBindViewHolder(final GitHubUserViewHolder holder, int position) {
+ GitHubUser gitHubUser = users.get(position);
+ holder.tvUserName.setText(gitHubUser.getLogin());
+ holder.tvUserUrl.setText(gitHubUser.getHtml_url());
+
+ Observable fetchImageObservable = Observable.just(gitHubUser).startWith(new GitHubUser())
+ .map(new Func1() {
+ @Override
+ public Bitmap call(GitHubUser gitHubUser) {
+ if(gitHubUser.getAvatar_url() == null) {
+ return BitmapFactory.decodeResource(holder.itemView.getContext().getResources(), R.mipmap.ic_launcher);
+ }
+
+ try {
+ URL url = new URL(gitHubUser.getAvatar_url());
+ return BitmapFactory.decodeStream(url.openConnection().getInputStream());
+ } catch (Exception e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+ })
+ .filter(new Func1() {
+ @Override
+ public Boolean call(Bitmap bitmap) {
+ return bitmap != null;
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+
+ Observable> fetchUserReposObservable = Observable.just(gitHubUser)
+ .map(new Func1>() {
+ @Override
+ public List call(GitHubUser gitHubUser) {
+ try {
+ return RestClient.getGitHubApi().getUserRepos(gitHubUser.getLogin()).execute().body();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.e(TAG, "call: ", e);
+ return null;
+ }
+ }
+ })
+ .filter(new Func1, Boolean>() {
+ @Override
+ public Boolean call(List gitHubRepos) {
+ return gitHubRepos != null;
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+
+// Observable randomReposObservable = RxView.clicks(holder.tvRepos).subscribeOn(AndroidSchedulers.mainThread());
+//
+// compositeSubscription.add(randomReposObservable.subscribe(new Subscriber() {
+// @Override
+// public void onCompleted() {
+// Log.d(TAG, "onCompleted: ");
+// }
+//
+// @Override
+// public void onError(Throwable e) {
+// Log.d(TAG, "onError: ");
+// }
+//
+// @Override
+// public void onNext(Void aVoid) {
+// Log.d(TAG, "onNext: ");
+// }
+// }));
+//
+// Observable> gitHubReposObservable = Observable.combineLatest(randomReposObservable, fetchUserReposObservable, new Func2, List>() {
+// @Override
+// public List call(Void aVoid, List gitHubRepos) {
+// return gitHubRepos;
+// }
+// }).subscribeOn(AndroidSchedulers.mainThread());
+
+ Observable> repoNamesObservable = fetchUserReposObservable.map(new Func1, List>() {
+ @Override
+ public List call(List gitHubRepos) {
+ List names = new ArrayList();
+ for (int i = 0; i < 3; i++) {
+ if(gitHubRepos.isEmpty()) break;
+ int position = new Random().nextInt(gitHubRepos.size());
+ names.add(gitHubRepos.get(position).getName());
+ gitHubRepos.remove(position);
+ }
+ return names;
+ }
+ });
+
+
+
+ compositeSubscription.add(fetchImageObservable.subscribe(new Subscriber() {
+ @Override
+ public void onCompleted() {
+ }
+ @Override
+ public void onError(Throwable e) {
+ }
+
+ @Override
+ public void onNext(Bitmap bitmap) {
+ holder.ivUser.setImageBitmap(bitmap);
+ }
+ }));
+
+ final ArrayAdapter stringArrayAdapter = new ArrayAdapter(holder.itemView.getContext(), R.layout.repo);
+ holder.lvRepos.setAdapter(stringArrayAdapter);
+
+ compositeSubscription.add(repoNamesObservable.subscribe(new Subscriber>() {
+ @Override
+ public void onCompleted() {
+
+ }
+
+ @Override
+ public void onError(Throwable e) {
+
+ }
+
+ @Override
+ public void onNext(List strings) {
+ stringArrayAdapter.clear();
+ stringArrayAdapter.addAll(strings);
+ stringArrayAdapter.notifyDataSetChanged();
+ }
+ }));
+ }
+
+ @Override
+ public int getItemCount() {
+ return users.size();
+ }
+
+ public class GitHubUserViewHolder extends RecyclerView.ViewHolder {
+ private ImageView ivUser;
+ private TextView tvUserName;
+ private TextView tvUserUrl;
+ private ListView lvRepos;
+
+ public GitHubUserViewHolder(View itemView) {
+ super(itemView);
+ ivUser = (ImageView) itemView.findViewById(R.id.ivUser);
+ tvUserName = (TextView) itemView.findViewById(R.id.tvUserName);
+ tvUserUrl = (TextView) itemView.findViewById(R.id.tvUserUrl);
+ lvRepos = (ListView) itemView.findViewById(R.id.lvRepos);
+ }
+ }
+
+ public void clearSubscriptions() {
+ if(!compositeSubscription.isUnsubscribed()) {
+ compositeSubscription.unsubscribe();
+ }
+ }
+}
diff --git a/app/src/main/java/com/ensarsarajcic/reactivegithubsample/views/MainActivity.java b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/views/MainActivity.java
new file mode 100644
index 0000000..2f12886
--- /dev/null
+++ b/app/src/main/java/com/ensarsarajcic/reactivegithubsample/views/MainActivity.java
@@ -0,0 +1,146 @@
+package com.ensarsarajcic.reactivegithubsample.views;
+
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.support.v7.widget.DefaultItemAnimator;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.widget.EditText;
+
+import com.ensarsarajcic.reactivegithubsample.R;
+import com.ensarsarajcic.reactivegithubsample.models.GitHubSearchResponse;
+import com.ensarsarajcic.reactivegithubsample.models.GitHubUser;
+import com.ensarsarajcic.reactivegithubsample.network.RestClient;
+import com.jakewharton.rxbinding.widget.RxTextView;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.TimeUnit;
+
+import rx.Observable;
+import rx.Subscriber;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.functions.Func1;
+import rx.schedulers.Schedulers;
+import rx.subscriptions.CompositeSubscription;
+
+public class MainActivity extends AppCompatActivity {
+ private static final String TAG = MainActivity.class.getSimpleName();
+
+ EditText etSearch;
+ RecyclerView rvUsers;
+
+ CompositeSubscription compositeSubscription;
+
+ Observable textChangeStream;
+ Observable> gitHubSearchResponseStream;
+ Observable> gitHubUsersResponseStream;
+ Observable> gitHubAllResponsesStream;
+
+ GitHubUsersAdapter adapter;
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+
+ etSearch = (EditText) findViewById(R.id.etSearch);
+ rvUsers = (RecyclerView) findViewById(R.id.rvUsers);
+
+ adapter = new GitHubUsersAdapter(new ArrayList());
+ rvUsers.setAdapter(adapter);
+ RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(getApplicationContext());
+ rvUsers.setLayoutManager(layoutManager);
+ rvUsers.setItemAnimator(new DefaultItemAnimator());
+
+ compositeSubscription = new CompositeSubscription();
+ textChangeStream = RxTextView.textChanges(etSearch).
+ debounce(1, TimeUnit.SECONDS).subscribeOn(AndroidSchedulers.mainThread());
+
+ gitHubSearchResponseStream = textChangeStream.filter(new Func1() {
+ @Override
+ public Boolean call(CharSequence charSequence) {
+ return !TextUtils.isEmpty(charSequence);
+ }
+ })
+ .map(new Func1() {
+ @Override
+ public GitHubSearchResponse call(CharSequence charSequence) {
+ try {
+ return RestClient.getGitHubApi().searchForUsers(charSequence.toString()).execute().body();
+ } catch (IOException ioException) {
+ ioException.printStackTrace();
+ return null;
+ }
+ }
+ })
+ .filter(new Func1() {
+ @Override
+ public Boolean call(GitHubSearchResponse gitHubSearchResponse) {
+ return gitHubSearchResponse != null;
+ }
+ })
+ .map(new Func1>() {
+ @Override
+ public List call(GitHubSearchResponse gitHubSearchResponse) {
+ return gitHubSearchResponse.getItems();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread());
+
+ gitHubUsersResponseStream = textChangeStream.
+ filter(new Func1() {
+ @Override
+ public Boolean call(CharSequence charSequence) {
+ return TextUtils.isEmpty(charSequence);
+ }
+ })
+ .map(new Func1>() {
+ @Override
+ public List call(CharSequence charSequence) {
+ try {
+ return RestClient.getGitHubApi().getUsers(new Random().nextInt(1000)).execute().body();
+ } catch (IOException ioException) {
+ ioException.printStackTrace();
+ return new ArrayList();
+ }
+ }
+ })
+ .subscribeOn(Schedulers.io()).
+ observeOn(AndroidSchedulers.mainThread());
+
+ gitHubAllResponsesStream = Observable.merge(gitHubSearchResponseStream, gitHubUsersResponseStream);
+
+ compositeSubscription.add(gitHubAllResponsesStream.subscribe(new Subscriber>() {
+ @Override
+ public void onCompleted() {
+
+ }
+
+ @Override
+ public void onError(Throwable e) {
+ e.printStackTrace();
+ }
+
+ @Override
+ public void onNext(List gitHubUsers) {
+ adapter.setItems(gitHubUsers);
+ adapter.notifyDataSetChanged();
+ }
+ }));
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ adapter.clearSubscriptions();
+ if(!compositeSubscription.isUnsubscribed()) {
+ compositeSubscription.unsubscribe();
+ }
+ }
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 624b2fd..01c8e51 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -8,10 +8,19 @@
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.ensarsarajcic.reactivegithubsample.MainActivity">
+ tools:context="com.ensarsarajcic.reactivegithubsample.views.MainActivity">
-
+ android:text="Search" />
+
+
diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml
new file mode 100644
index 0000000..4116488
--- /dev/null
+++ b/app/src/main/res/layout/list_item.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/repo.xml b/app/src/main/res/layout/repo.xml
new file mode 100644
index 0000000..69fb6b5
--- /dev/null
+++ b/app/src/main/res/layout/repo.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 3ab3e9c..2e7d32b 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,6 @@
- #3F51B5
+ #123DE1
#303F9F
- #FF4081
+ #22AACC