From b1db59fda2031cc43da6663144998f457ffc4f97 Mon Sep 17 00:00:00 2001 From: Alex Ning Date: Sat, 23 Feb 2019 12:04:16 +0800 Subject: [PATCH] Add a feature: Search for users. Change some names of interfaces and methods. --- .idea/caches/build_file_checksums.ser | Bin 533 -> 533 bytes .idea/caches/gradle_models.ser | Bin 303289 -> 303304 bytes .../CheckIsFollowingUserAsyncTask.java | 41 ++++ .../infinityforreddit/FetchUserData.java | 53 +++- .../LoadUserDataAsyncTask.java | 2 +- .../infinityforreddit/NetworkComponent.java | 1 + .../infinityforreddit/ParseUserData.java | 89 ++++++- .../infinityforreddit/RedditAPI.java | 5 +- .../infinityforreddit/SearchActivity.java | 5 +- .../SubredditListingRecyclerViewAdapter.java | 2 - .../SubredditSubscription.java | 12 - .../infinityforreddit/UserFollowing.java | 2 +- .../UserListingDataSource.java | 107 ++++++++ .../UserListingDataSourceFactory.java | 40 +++ .../UserListingFragment.java | 147 +++++++++++ .../UserListingRecyclerViewAdapter.java | 231 ++++++++++++++++++ .../UserListingViewModel.java | 79 ++++++ .../ViewSubredditDetailActivity.java | 50 ---- .../ViewUserDetailActivity.java | 25 +- .../main/res/layout/fragment_user_listing.xml | 47 ++++ app/src/main/res/layout/item_user_listing.xml | 48 ++++ app/src/main/res/values/strings.xml | 3 +- 22 files changed, 892 insertions(+), 97 deletions(-) create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/CheckIsFollowingUserAsyncTask.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/UserListingDataSource.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/UserListingDataSourceFactory.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/UserListingFragment.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/UserListingRecyclerViewAdapter.java create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/UserListingViewModel.java create mode 100644 app/src/main/res/layout/fragment_user_listing.xml create mode 100644 app/src/main/res/layout/item_user_listing.xml diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 401fbdb551bf57c0333628b26dbe3abbd4dc0643..283d42fff8a41f22d08ed0dddbf5b1c2856c94dd 100644 GIT binary patch delta 15 WcmbQrGL>b*3>JwpR^g3vDj5MM2?Wdl delta 15 XcmbQrGL>b*3>ML-7rh(jR5AhpE#U?V diff --git a/.idea/caches/gradle_models.ser b/.idea/caches/gradle_models.ser index 51f7172ab46234dcbe86ff9bf2248e5f87fd2d0d..8c8664630918a3fdb949af594540672f9003e7a6 100644 GIT binary patch delta 48037 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 delta 48090 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 diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/CheckIsFollowingUserAsyncTask.java b/app/src/main/java/ml/docilealligator/infinityforreddit/CheckIsFollowingUserAsyncTask.java new file mode 100644 index 00000000..4d75d155 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/CheckIsFollowingUserAsyncTask.java @@ -0,0 +1,41 @@ +package ml.docilealligator.infinityforreddit; + +import android.os.AsyncTask; + +import SubscribedUserDatabase.SubscribedUserDao; +import SubscribedUserDatabase.SubscribedUserData; + +public class CheckIsFollowingUserAsyncTask extends AsyncTask { + private SubscribedUserDao subscribedUserDao; + private String userName; + private SubscribedUserData subscribedUserData; + private CheckIsFollowingUserListener checkIsFollowingUserListener; + + interface CheckIsFollowingUserListener { + void isSubscribed(); + void isNotSubscribed(); + } + + CheckIsFollowingUserAsyncTask(SubscribedUserDao subscribedUserDao, String userName, + CheckIsFollowingUserListener checkIsFollowingUserListener) { + this.subscribedUserDao = subscribedUserDao; + this.userName = userName; + this.checkIsFollowingUserListener = checkIsFollowingUserListener; + } + + @Override + protected Void doInBackground(Void... voids) { + subscribedUserData = subscribedUserDao.getSubscribedUser(userName); + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + if(subscribedUserData != null) { + checkIsFollowingUserListener.isSubscribed(); + } else { + checkIsFollowingUserListener.isNotSubscribed(); + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/FetchUserData.java b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchUserData.java index 7415be7e..7f2e2528 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/FetchUserData.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchUserData.java @@ -1,9 +1,11 @@ package ml.docilealligator.infinityforreddit; -import androidx.annotation.NonNull; import android.util.Log; +import java.util.ArrayList; + import User.UserData; +import androidx.annotation.NonNull; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Retrofit; @@ -11,7 +13,12 @@ import retrofit2.Retrofit; public class FetchUserData { public interface FetchUserDataListener { void onFetchUserDataSuccess(UserData userData); - void onFetchUserDataFail(); + void onFetchUserDataFailed(); + } + + public interface FetchUserListingDataListener { + void onFetchUserListingDataSuccess(ArrayList userData, String after); + void onFetchUserListingDataFailed(); } public static void fetchUserData(final Retrofit retrofit, String userName, @@ -30,20 +37,54 @@ public class FetchUserData { } @Override - public void onParseUserDataFail() { - fetchUserDataListener.onFetchUserDataFail(); + public void onParseUserDataFailed() { + fetchUserDataListener.onFetchUserDataFailed(); } }); } else { Log.i("call failed", response.message()); - fetchUserDataListener.onFetchUserDataFail(); + fetchUserDataListener.onFetchUserDataFailed(); } } @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { Log.i("call failed", t.getMessage()); - fetchUserDataListener.onFetchUserDataFail(); + fetchUserDataListener.onFetchUserDataFailed(); + } + }); + } + + public static void fetchUserListingData(Retrofit retrofit, String query, String after, + FetchUserListingDataListener fetchUserListingDataListener) { + RedditAPI api = retrofit.create(RedditAPI.class); + + Call userInfo = api.searchUsers(query, after); + userInfo.enqueue(new Callback() { + @Override + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { + if(response.isSuccessful()) { + ParseUserData.parseUserListingData(response.body(), new ParseUserData.ParseUserListingDataListener() { + @Override + public void onParseUserListingDataSuccess(ArrayList userData, String after) { + fetchUserListingDataListener.onFetchUserListingDataSuccess(userData, after); + } + + @Override + public void onParseUserListingDataFailed() { + fetchUserListingDataListener.onFetchUserListingDataFailed(); + } + }); + } else { + Log.i("call failed", response.message()); + fetchUserListingDataListener.onFetchUserListingDataFailed(); + } + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + Log.i("call failed", t.getMessage()); + fetchUserListingDataListener.onFetchUserListingDataFailed(); } }); } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/LoadUserDataAsyncTask.java b/app/src/main/java/ml/docilealligator/infinityforreddit/LoadUserDataAsyncTask.java index 704c3e08..8cb576d2 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/LoadUserDataAsyncTask.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/LoadUserDataAsyncTask.java @@ -49,7 +49,7 @@ public class LoadUserDataAsyncTask extends AsyncTask { } @Override - public void onFetchUserDataFail() { + public void onFetchUserDataFailed() { loadUserDataAsyncTaskListener.loadUserDataSuccess(""); } }); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/NetworkComponent.java b/app/src/main/java/ml/docilealligator/infinityforreddit/NetworkComponent.java index 05aae8af..b250ac29 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/NetworkComponent.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/NetworkComponent.java @@ -10,6 +10,7 @@ interface NetworkComponent { void inject(MainActivity mainActivity); void inject(PostFragment postFragment); void inject(SubredditListingFragment subredditListingFragment); + void inject(UserListingFragment userListingFragment); void inject(ViewPostDetailActivity viewPostDetailActivity); void inject(ViewSubredditDetailActivity viewSubredditDetailActivity); void inject(ViewUserDetailActivity viewUserDetailActivity); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/ParseUserData.java b/app/src/main/java/ml/docilealligator/infinityforreddit/ParseUserData.java index 10d118cf..618a9a81 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/ParseUserData.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/ParseUserData.java @@ -3,21 +3,33 @@ package ml.docilealligator.infinityforreddit; import android.os.AsyncTask; import android.util.Log; +import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import java.util.ArrayList; + import User.UserData; public class ParseUserData { interface ParseUserDataListener { void onParseUserDataSuccess(UserData userData); - void onParseUserDataFail(); + void onParseUserDataFailed(); + } + + interface ParseUserListingDataListener { + void onParseUserListingDataSuccess(ArrayList userData, String after); + void onParseUserListingDataFailed(); } static void parseUserData(String response, ParseUserDataListener parseUserDataListener) { new ParseUserDataAsyncTask(response, parseUserDataListener).execute(); } + static void parseUserListingData(String response, ParseUserListingDataListener parseUserListingDataListener) { + new ParseUserListingDataAsyncTask(response, parseUserListingDataListener).execute(); + } + private static class ParseUserDataAsyncTask extends AsyncTask { private JSONObject jsonResponse; private ParseUserDataListener parseUserDataListener; @@ -32,14 +44,15 @@ public class ParseUserData { parseFailed = false; } catch (JSONException e) { Log.i("userdata json error", e.getMessage()); - parseUserDataListener.onParseUserDataFail(); + parseUserDataListener.onParseUserDataFailed(); } } @Override protected Void doInBackground(Void... voids) { try { - jsonResponse = jsonResponse.getJSONObject(JSONUtils.DATA_KEY); + userData = parseUserDataBase(jsonResponse); + /*jsonResponse = jsonResponse.getJSONObject(JSONUtils.DATA_KEY); String userName = jsonResponse.getString(JSONUtils.NAME_KEY); String iconImageUrl = jsonResponse.getString(JSONUtils.ICON_IMG_KEY); String bannerImageUrl = ""; @@ -56,7 +69,7 @@ public class ParseUserData { boolean isGold = jsonResponse.getBoolean(JSONUtils.IS_GOLD_KEY); boolean isFriend = jsonResponse.getBoolean(JSONUtils.IS_FRIEND_KEY); - userData = new UserData(userName, iconImageUrl, bannerImageUrl, karma, isGold, isFriend, canBeFollowed); + userData = new UserData(userName, iconImageUrl, bannerImageUrl, karma, isGold, isFriend, canBeFollowed);*/ } catch (JSONException e) { parseFailed = true; Log.i("parse user data error", e.getMessage()); @@ -69,8 +82,74 @@ public class ParseUserData { if(!parseFailed) { parseUserDataListener.onParseUserDataSuccess(userData); } else { - parseUserDataListener.onParseUserDataFail(); + parseUserDataListener.onParseUserDataFailed(); } } } + + private static class ParseUserListingDataAsyncTask extends AsyncTask { + private JSONObject jsonResponse; + private ParseUserListingDataListener parseUserListingDataListener; + private String after; + private boolean parseFailed; + + private ArrayList userDataArrayList; + + ParseUserListingDataAsyncTask(String response, ParseUserListingDataListener parseUserListingDataListener){ + try { + jsonResponse = new JSONObject(response); + this.parseUserListingDataListener = parseUserListingDataListener; + parseFailed = false; + userDataArrayList = new ArrayList<>(); + } catch (JSONException e) { + Log.i("userdata json error", e.getMessage()); + this.parseUserListingDataListener.onParseUserListingDataFailed(); + } + } + + @Override + protected Void doInBackground(Void... voids) { + try { + after = jsonResponse.getJSONObject(JSONUtils.DATA_KEY).getString(JSONUtils.AFTER_KEY); + JSONArray children = jsonResponse.getJSONObject(JSONUtils.DATA_KEY).getJSONArray(JSONUtils.CHILDREN_KEY); + for(int i = 0; i < children.length(); i++) { + userDataArrayList.add(parseUserDataBase(children.getJSONObject(i))); + } + } catch (JSONException e) { + parseFailed = true; + Log.i("parse user data error", e.getMessage()); + } + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + if(!parseFailed) { + parseUserListingDataListener.onParseUserListingDataSuccess(userDataArrayList, after); + } else { + parseUserListingDataListener.onParseUserListingDataFailed(); + } + } + } + + private static UserData parseUserDataBase(JSONObject userDataJson) throws JSONException { + userDataJson = userDataJson.getJSONObject(JSONUtils.DATA_KEY); + String userName = userDataJson.getString(JSONUtils.NAME_KEY); + String iconImageUrl = userDataJson.getString(JSONUtils.ICON_IMG_KEY); + String bannerImageUrl = ""; + boolean canBeFollowed; + if(userDataJson.has(JSONUtils.SUBREDDIT_KEY) && !userDataJson.isNull(JSONUtils.SUBREDDIT_KEY)) { + bannerImageUrl = userDataJson.getJSONObject(JSONUtils.SUBREDDIT_KEY).getString(JSONUtils.BANNER_IMG_KEY); + canBeFollowed = true; + } else { + canBeFollowed = false; + } + int linkKarma = userDataJson.getInt(JSONUtils.LINK_KARMA_KEY); + int commentKarma = userDataJson.getInt(JSONUtils.COMMENT_KARMA_KEY); + int karma = linkKarma + commentKarma; + boolean isGold = userDataJson.getBoolean(JSONUtils.IS_GOLD_KEY); + boolean isFriend = userDataJson.getBoolean(JSONUtils.IS_FRIEND_KEY); + + return new UserData(userName, iconImageUrl, bannerImageUrl, karma, isGold, isFriend, canBeFollowed); + } } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java b/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java index e467eb0a..a0118ded 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java @@ -60,9 +60,8 @@ public interface RedditAPI { @GET("subreddits/search.json?raw_json=1&include_over_18=on") Call searchSubreddits(@Query("q") String subredditName, @Query("after") String after); - @GET("profiles/search.json?raw_json=1") - Call searchProfiles(@Query("q") String profileName, @Query("after") String after, - @HeaderMap Map headers); + @GET("search.json?raw_json=1&type=user") + Call searchUsers(@Query("q") String profileName, @Query("after") String after); @GET("search.json?raw_json=1&type=link") Call searchPosts(@Query("q") String query, @Query("after") String after, diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/SearchActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/SearchActivity.java index dd72822e..25a78b9d 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/SearchActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/SearchActivity.java @@ -78,10 +78,9 @@ public class SearchActivity extends AppCompatActivity { } default: { - PostFragment mFragment = new PostFragment(); + UserListingFragment mFragment = new UserListingFragment(); Bundle bundle = new Bundle(); - bundle.putInt(PostFragment.POST_TYPE_KEY, PostDataSource.TYPE_FRONT_PAGE); - bundle.putString(PostFragment.NAME_KEY, mQuery); + bundle.putString(UserListingFragment.QUERY_KEY, mQuery); mFragment.setArguments(bundle); return mFragment; } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/SubredditListingRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/SubredditListingRecyclerViewAdapter.java index 56735c5e..e598d786 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/SubredditListingRecyclerViewAdapter.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/SubredditListingRecyclerViewAdapter.java @@ -228,6 +228,4 @@ public class SubredditListingRecyclerViewAdapter extends PagedListAdapter { + interface OnUserListingDataFetchedCallback { + void hasUser(); + void noUser(); + } + private Retrofit retrofit; + private String query; + private UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback; + + private MutableLiveData paginationNetworkStateLiveData; + private MutableLiveData initialLoadStateLiveData; + + private PageKeyedDataSource.LoadInitialParams initialParams; + private PageKeyedDataSource.LoadInitialCallback initialCallback; + private PageKeyedDataSource.LoadParams params; + private PageKeyedDataSource.LoadCallback callback; + + UserListingDataSource(Retrofit retrofit, String query, + UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback) { + this.retrofit = retrofit; + this.query = query; + this.onUserListingDataFetchedCallback = onUserListingDataFetchedCallback; + paginationNetworkStateLiveData = new MutableLiveData(); + initialLoadStateLiveData = new MutableLiveData(); + } + + MutableLiveData getPaginationNetworkStateLiveData() { + return paginationNetworkStateLiveData; + } + + MutableLiveData getInitialLoadStateLiveData() { + return initialLoadStateLiveData; + } + + @Override + public void loadInitial(@NonNull PageKeyedDataSource.LoadInitialParams params, @NonNull PageKeyedDataSource.LoadInitialCallback callback) { + initialParams = params; + initialCallback = callback; + + initialLoadStateLiveData.postValue(NetworkState.LOADING); + + FetchUserData.fetchUserListingData(retrofit, query, null, new FetchUserData.FetchUserListingDataListener() { + @Override + public void onFetchUserListingDataSuccess(ArrayList UserData, String after) { + if(UserData.size() == 0) { + onUserListingDataFetchedCallback.noUser(); + } else { + onUserListingDataFetchedCallback.hasUser(); + } + + callback.onResult(UserData, null, after); + initialLoadStateLiveData.postValue(NetworkState.LOADED); + } + + @Override + public void onFetchUserListingDataFailed() { + initialLoadStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error retrieving User list")); + } + }); + } + + @Override + public void loadBefore(@NonNull PageKeyedDataSource.LoadParams params, @NonNull PageKeyedDataSource.LoadCallback callback) { + + } + + @Override + public void loadAfter(@NonNull PageKeyedDataSource.LoadParams params, @NonNull PageKeyedDataSource.LoadCallback callback) { + this.params = params; + this.callback = callback; + + if(params.key.equals("null")) { + return; + } + + FetchUserData.fetchUserListingData(retrofit, query, params.key, new FetchUserData.FetchUserListingDataListener() { + @Override + public void onFetchUserListingDataSuccess(ArrayList UserData, String after) { + callback.onResult(UserData, after); + paginationNetworkStateLiveData.postValue(NetworkState.LOADED); + } + + @Override + public void onFetchUserListingDataFailed() { + paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error retrieving User list")); + } + }); + } + + void retry() { + loadInitial(initialParams, initialCallback); + } + + void retryLoadingMore() { + loadAfter(params, callback); + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingDataSourceFactory.java b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingDataSourceFactory.java new file mode 100644 index 00000000..6498412c --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingDataSourceFactory.java @@ -0,0 +1,40 @@ +package ml.docilealligator.infinityforreddit; + +import androidx.annotation.NonNull; +import androidx.lifecycle.MutableLiveData; +import androidx.paging.DataSource; +import retrofit2.Retrofit; + +public class UserListingDataSourceFactory extends DataSource.Factory { + private Retrofit retrofit; + private String query; + private UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback; + + private UserListingDataSource UserListingDataSource; + private MutableLiveData UserListingDataSourceMutableLiveData; + + UserListingDataSourceFactory(Retrofit retrofit, String query, + UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback) { + this.retrofit = retrofit; + this.query = query; + this.onUserListingDataFetchedCallback = onUserListingDataFetchedCallback; + UserListingDataSourceMutableLiveData = new MutableLiveData<>(); + } + + @NonNull + @Override + public DataSource create() { + UserListingDataSource UserListingDataSource = new UserListingDataSource(retrofit, + query, onUserListingDataFetchedCallback); + UserListingDataSourceMutableLiveData.postValue(UserListingDataSource); + return UserListingDataSource; + } + + public MutableLiveData getUserListingDataSourceMutableLiveData() { + return UserListingDataSourceMutableLiveData; + } + + UserListingDataSource getUserListingDataSource() { + return UserListingDataSource; + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingFragment.java b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingFragment.java new file mode 100644 index 00000000..abc9dcdb --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingFragment.java @@ -0,0 +1,147 @@ +package ml.docilealligator.infinityforreddit; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.lsjwzh.widget.materialloadingprogressbar.CircleProgressBar; + +import javax.inject.Inject; +import javax.inject.Named; + +import SubscribedUserDatabase.SubscribedUserRoomDatabase; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProviders; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; +import butterknife.ButterKnife; +import retrofit2.Retrofit; + + +/** + * A simple {@link Fragment} subclass. + */ +public class UserListingFragment extends Fragment { + static final String QUERY_KEY = "QK"; + + @BindView(R.id.coordinator_layout_user_listing_fragment) + CoordinatorLayout mCoordinatorLayout; + @BindView(R.id.recycler_view_user_listing_fragment) + RecyclerView mUserListingRecyclerView; + @BindView(R.id.progress_bar_user_listing_fragment) + CircleProgressBar mProgressBar; + @BindView(R.id.fetch_user_listing_info_linear_layout_user_listing_fragment) + LinearLayout mFetchUserListingInfoLinearLayout; + @BindView(R.id.fetch_user_listing_info_image_view_user_listing_fragment) + ImageView mFetchUserListingInfoImageView; + @BindView(R.id.fetch_user_listing_info_text_view_user_listing_fragment) + TextView mFetchUserListingInfoTextView; + + private LinearLayoutManager mLinearLayoutManager; + + private String mQuery; + + private UserListingRecyclerViewAdapter mAdapter; + + UserListingViewModel mUserListingViewModel; + + @Inject + @Named("auth_info") + SharedPreferences mAuthInfoSharedPreferences; + + @Inject @Named("no_oauth") + Retrofit mRetrofit; + + @Inject @Named("oauth") + Retrofit mOauthRetrofit; + + public UserListingFragment() { + // Required empty public constructor + } + + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + // Inflate the layout for this fragment + View rootView = inflater.inflate(R.layout.fragment_user_listing, container, false); + + ((Infinity) getActivity().getApplication()).getmNetworkComponent().inject(this); + + ButterKnife.bind(this, rootView); + + mLinearLayoutManager = new LinearLayoutManager(getActivity()); + mUserListingRecyclerView.setLayoutManager(mLinearLayoutManager); + + mQuery = getArguments().getString(QUERY_KEY); + + String accessToken = getActivity().getSharedPreferences(SharedPreferencesUtils.AUTH_CODE_FILE_KEY, Context.MODE_PRIVATE) + .getString(SharedPreferencesUtils.ACCESS_TOKEN_KEY, ""); + + UserListingViewModel.Factory factory = new UserListingViewModel.Factory(mRetrofit, mQuery, + new UserListingDataSource.OnUserListingDataFetchedCallback() { + @Override + public void hasUser() { + mFetchUserListingInfoLinearLayout.setVisibility(View.GONE); + } + + @Override + public void noUser() { + mFetchUserListingInfoLinearLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + //Do nothing + } + }); + showErrorView(R.string.no_users); + } + }); + + mAdapter = new UserListingRecyclerViewAdapter(getActivity(), mOauthRetrofit, mRetrofit, + mAuthInfoSharedPreferences, + SubscribedUserRoomDatabase.getDatabase(getContext()).subscribedUserDao(), + () -> mUserListingViewModel.retryLoadingMore()); + + mUserListingRecyclerView.setAdapter(mAdapter); + + mUserListingViewModel = ViewModelProviders.of(this, factory).get(UserListingViewModel.class); + mUserListingViewModel.getUsers().observe(this, UserData -> mAdapter.submitList(UserData)); + + mUserListingViewModel.getInitialLoadingState().observe(this, networkState -> { + if(networkState.getStatus().equals(NetworkState.Status.SUCCESS)) { + mProgressBar.setVisibility(View.GONE); + } else if(networkState.getStatus().equals(NetworkState.Status.FAILED)) { + mFetchUserListingInfoLinearLayout.setOnClickListener(view -> mUserListingViewModel.retry()); + showErrorView(R.string.load_posts_error); + } else { + mFetchUserListingInfoLinearLayout.setVisibility(View.GONE); + mProgressBar.setVisibility(View.VISIBLE); + } + }); + + mUserListingViewModel.getPaginationNetworkState().observe(this, networkState -> { + mAdapter.setNetworkState(networkState); + }); + + return rootView; + } + + private void showErrorView(int stringResId) { + mProgressBar.setVisibility(View.GONE); + if(getActivity() != null && isAdded()) { + mFetchUserListingInfoLinearLayout.setVisibility(View.VISIBLE); + mFetchUserListingInfoTextView.setText(stringResId); + Glide.with(this).load(R.drawable.load_post_error_indicator).into(mFetchUserListingInfoImageView); + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingRecyclerViewAdapter.java new file mode 100644 index 00000000..e48bfba5 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingRecyclerViewAdapter.java @@ -0,0 +1,231 @@ +package ml.docilealligator.infinityforreddit; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.ProgressBar; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.request.RequestOptions; + +import SubscribedUserDatabase.SubscribedUserDao; +import User.UserData; +import androidx.annotation.NonNull; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.paging.PagedListAdapter; +import androidx.recyclerview.widget.DiffUtil; +import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; +import butterknife.ButterKnife; +import jp.wasabeef.glide.transformations.RoundedCornersTransformation; +import pl.droidsonroids.gif.GifImageView; +import retrofit2.Retrofit; + +public class UserListingRecyclerViewAdapter extends PagedListAdapter { + interface RetryLoadingMoreCallback { + void retryLoadingMore(); + } + + private RequestManager glide; + + private static final int VIEW_TYPE_DATA = 0; + private static final int VIEW_TYPE_ERROR = 1; + private static final int VIEW_TYPE_LOADING = 2; + + private Context context; + private Retrofit oauthRetrofit; + private Retrofit retrofit; + private SharedPreferences authInfoSharedPreferences; + private SubscribedUserDao subscribedUserDao; + + private NetworkState networkState; + private UserListingRecyclerViewAdapter.RetryLoadingMoreCallback retryLoadingMoreCallback; + + UserListingRecyclerViewAdapter(Context context, Retrofit oauthRetrofit, Retrofit retrofit, + SharedPreferences authInfoSharedPreferences, + SubscribedUserDao subscribedUserDao, + UserListingRecyclerViewAdapter.RetryLoadingMoreCallback retryLoadingMoreCallback) { + super(DIFF_CALLBACK); + this.context = context; + this.oauthRetrofit = oauthRetrofit; + this.retrofit = retrofit; + this.authInfoSharedPreferences = authInfoSharedPreferences; + this.subscribedUserDao = subscribedUserDao; + this.retryLoadingMoreCallback = retryLoadingMoreCallback; + glide = Glide.with(context.getApplicationContext()); + } + + static final DiffUtil.ItemCallback DIFF_CALLBACK = new DiffUtil.ItemCallback() { + @Override + public boolean areItemsTheSame(@NonNull UserData oldItem, @NonNull UserData newItem) { + return oldItem.getName().equals(newItem.getName()); + } + + @Override + public boolean areContentsTheSame(@NonNull UserData oldItem, @NonNull UserData newItem) { + return true; + } + }; + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + if(viewType == VIEW_TYPE_DATA) { + ConstraintLayout constraintLayout = (ConstraintLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_user_listing, parent, false); + return new UserListingRecyclerViewAdapter.DataViewHolder(constraintLayout); + } else if(viewType == VIEW_TYPE_ERROR) { + RelativeLayout relativeLayout = (RelativeLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_footer_error, parent, false); + return new UserListingRecyclerViewAdapter.ErrorViewHolder(relativeLayout); + } else { + RelativeLayout relativeLayout = (RelativeLayout) LayoutInflater.from(parent.getContext()).inflate(R.layout.item_footer_loading, parent, false); + return new UserListingRecyclerViewAdapter.LoadingViewHolder(relativeLayout); + } + } + + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { + if(holder instanceof UserListingRecyclerViewAdapter.DataViewHolder) { + UserData UserData = getItem(position); + ((UserListingRecyclerViewAdapter.DataViewHolder) holder).constraintLayout.setOnClickListener(view -> { + Intent intent = new Intent(context, ViewUserDetailActivity.class); + intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, UserData.getName()); + context.startActivity(intent); + }); + + if(UserData.getIconUrl() != null) { + glide.load(UserData.getIconUrl()) + .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) + .error(glide.load(R.drawable.subreddit_default_icon)) + .into(((UserListingRecyclerViewAdapter.DataViewHolder) holder).iconGifImageView); + } else { + glide.load(R.drawable.subreddit_default_icon) + .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) + .into(((UserListingRecyclerViewAdapter.DataViewHolder) holder).iconGifImageView); + } + + ((UserListingRecyclerViewAdapter.DataViewHolder) holder).UserNameTextView.setText(UserData.getName()); + + new CheckIsFollowingUserAsyncTask(subscribedUserDao, UserData.getName(), + new CheckIsFollowingUserAsyncTask.CheckIsFollowingUserListener() { + @Override + public void isSubscribed() { + ((UserListingRecyclerViewAdapter.DataViewHolder) holder).subscribeButton.setVisibility(View.GONE); + } + + @Override + public void isNotSubscribed() { + ((UserListingRecyclerViewAdapter.DataViewHolder) holder).subscribeButton.setVisibility(View.VISIBLE); + ((UserListingRecyclerViewAdapter.DataViewHolder) holder).subscribeButton.setOnClickListener(view -> { + UserFollowing.followUser(oauthRetrofit, retrofit, + authInfoSharedPreferences, UserData.getName(), subscribedUserDao, + new UserFollowing.UserFollowingListener() { + @Override + public void onUserFollowingSuccess() { + ((UserListingRecyclerViewAdapter.DataViewHolder) holder).subscribeButton.setVisibility(View.GONE); + Toast.makeText(context, R.string.followed, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onUserFollowingFail() { + Toast.makeText(context, R.string.follow_failed, Toast.LENGTH_SHORT).show(); + } + }); + }); + } + }).execute(); + } + } + + @Override + public int getItemViewType(int position) { + // Reached at the end + if (hasExtraRow() && position == getItemCount() - 1) { + if (networkState.getStatus() == NetworkState.Status.LOADING) { + return VIEW_TYPE_LOADING; + } else { + return VIEW_TYPE_ERROR; + } + } else { + return VIEW_TYPE_DATA; + } + } + + @Override + public int getItemCount() { + if(hasExtraRow()) { + return super.getItemCount() + 1; + } + return super.getItemCount(); + } + + private boolean hasExtraRow() { + return networkState != null && networkState.getStatus() != NetworkState.Status.SUCCESS; + } + + void setNetworkState(NetworkState newNetworkState) { + NetworkState previousState = this.networkState; + boolean previousExtraRow = hasExtraRow(); + this.networkState = newNetworkState; + boolean newExtraRow = hasExtraRow(); + if (previousExtraRow != newExtraRow) { + if (previousExtraRow) { + notifyItemRemoved(super.getItemCount()); + } else { + notifyItemInserted(super.getItemCount()); + } + } else if (newExtraRow && !previousState.equals(newNetworkState)) { + notifyItemChanged(getItemCount() - 1); + } + } + + class DataViewHolder extends RecyclerView.ViewHolder { + @BindView(R.id.constraint_layout_item_user_listing) ConstraintLayout constraintLayout; + @BindView(R.id.user_icon_gif_image_view_item_user_listing) GifImageView iconGifImageView; + @BindView(R.id.user_name_text_view_item_user_listing) TextView UserNameTextView; + @BindView(R.id.subscribe_image_view_item_user_listing) ImageView subscribeButton; + + DataViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + } + + class ErrorViewHolder extends RecyclerView.ViewHolder { + @BindView(R.id.relative_layout_footer_error_item) RelativeLayout relativeLayout; + @BindView(R.id.error_text_view_footer_error_item) TextView errorTextView; + @BindView(R.id.retry_button_footer_error_item) Button retryButton; + + ErrorViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + retryButton.setOnClickListener(view -> retryLoadingMoreCallback.retryLoadingMore()); + errorTextView.setText(R.string.load_comment_failed); + } + } + + class LoadingViewHolder extends RecyclerView.ViewHolder { + @BindView(R.id.progress_bar_footer_progress_bar_item) ProgressBar progressBar; + + LoadingViewHolder(@NonNull View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + } + + @Override + public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { + if(holder instanceof UserListingRecyclerViewAdapter.DataViewHolder) { + glide.clear(((UserListingRecyclerViewAdapter.DataViewHolder) holder).iconGifImageView); + ((UserListingRecyclerViewAdapter.DataViewHolder) holder).subscribeButton.setVisibility(View.GONE); + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingViewModel.java b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingViewModel.java new file mode 100644 index 00000000..6a1804db --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingViewModel.java @@ -0,0 +1,79 @@ +package ml.docilealligator.infinityforreddit; + +import User.UserData; +import androidx.annotation.NonNull; +import androidx.arch.core.util.Function; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.Transformations; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import androidx.paging.LivePagedListBuilder; +import androidx.paging.PagedList; +import retrofit2.Retrofit; + +public class UserListingViewModel extends ViewModel { + private UserListingDataSourceFactory UserListingDataSourceFactory; + private LiveData paginationNetworkState; + private LiveData initialLoadingState; + private LiveData> Users; + + UserListingViewModel(Retrofit retrofit, String query, + UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback) { + UserListingDataSourceFactory = new UserListingDataSourceFactory(retrofit, query, onUserListingDataFetchedCallback); + + initialLoadingState = Transformations.switchMap(UserListingDataSourceFactory.getUserListingDataSourceMutableLiveData(), + (Function>) UserListingDataSource::getInitialLoadStateLiveData); + paginationNetworkState = Transformations.switchMap(UserListingDataSourceFactory.getUserListingDataSourceMutableLiveData(), + (Function>) UserListingDataSource::getPaginationNetworkStateLiveData); + PagedList.Config pagedListConfig = + (new PagedList.Config.Builder()) + .setEnablePlaceholders(false) + .setPageSize(25) + .build(); + + Users = (new LivePagedListBuilder(UserListingDataSourceFactory, pagedListConfig)).build(); + } + + LiveData> getUsers() { + return Users; + } + + LiveData getPaginationNetworkState() { + return paginationNetworkState; + } + + LiveData getInitialLoadingState() { + return initialLoadingState; + } + + void refresh() { + UserListingDataSourceFactory.getUserListingDataSource().invalidate(); + } + + void retry() { + UserListingDataSourceFactory.getUserListingDataSource().retry(); + } + + void retryLoadingMore() { + UserListingDataSourceFactory.getUserListingDataSource().retryLoadingMore(); + } + + public static class Factory extends ViewModelProvider.NewInstanceFactory { + private Retrofit retrofit; + private String query; + private UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback; + + public Factory(Retrofit retrofit, String query, + UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback) { + this.retrofit = retrofit; + this.query = query; + this.onUserListingDataFetchedCallback = onUserListingDataFetchedCallback; + } + + @NonNull + @Override + public T create(@NonNull Class modelClass) { + return (T) new UserListingViewModel(retrofit, query, onUserListingDataFetchedCallback); + } + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewSubredditDetailActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewSubredditDetailActivity.java index b4127233..bce27cc8 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewSubredditDetailActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewSubredditDetailActivity.java @@ -248,20 +248,6 @@ public class ViewSubredditDetailActivity extends AppCompatActivity { .execute(); String nOnlineSubscribers = getString(R.string.online_subscribers_number_detail, nCurrentOnlineSubscribers); nOnlineSubscribersTextView.setText(nOnlineSubscribers); - /*ParseSubredditData.parseSubredditData(response, new ParseSubredditData.ParseSubredditDataListener() { - @Override - public void onParseSubredditDataSuccess(SubredditData subredditData, int nCurrentOnlineSubscribers) { - new InsertSubredditDataAsyncTask(SubredditRoomDatabase.getDatabase(ViewSubredditDetailActivity.this), subredditData) - .execute(); - String nOnlineSubscribers = getString(R.string.online_subscribers_number_detail, nCurrentOnlineSubscribers); - nOnlineSubscribersTextView.setText(nOnlineSubscribers); - } - - @Override - public void onParseSubredditDataFail() { - makeSnackbar(R.string.cannot_fetch_subreddit_info); - } - });*/ } @Override @@ -331,40 +317,4 @@ public class ViewSubredditDetailActivity extends AppCompatActivity { return null; } } - - /*private static class CheckIsSubscribedToSubredditAsyncTask extends AsyncTask { - - private SubscribedSubredditDao subscribedSubredditDao; - private String subredditName; - private SubscribedSubredditData subscribedSubredditData; - private CheckIsSubscribedToSubredditListener checkIsSubscribedToSubredditListener; - - interface CheckIsSubscribedToSubredditListener { - void isSubscribed(); - void isNotSubscribed(); - } - - CheckIsSubscribedToSubredditAsyncTask(SubscribedSubredditDao subscribedSubredditDao, String subredditName, - CheckIsSubscribedToSubredditListener checkIsSubscribedToSubredditListener) { - this.subscribedSubredditDao = subscribedSubredditDao; - this.subredditName =subredditName; - this.checkIsSubscribedToSubredditListener = checkIsSubscribedToSubredditListener; - } - - @Override - protected Void doInBackground(Void... voids) { - subscribedSubredditData = subscribedSubredditDao.getSubscribedSubreddit(subredditName); - return null; - } - - @Override - protected void onPostExecute(Void aVoid) { - super.onPostExecute(aVoid); - if(subscribedSubredditData != null) { - checkIsSubscribedToSubredditListener.isSubscribed(); - } else { - checkIsSubscribedToSubredditListener.isNotSubscribed(); - } - } - }*/ } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewUserDetailActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewUserDetailActivity.java index c5dedf11..eacca302 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewUserDetailActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewUserDetailActivity.java @@ -1,18 +1,9 @@ package ml.docilealligator.infinityforreddit; -import androidx.lifecycle.ViewModelProviders; import android.content.Intent; import android.content.SharedPreferences; import android.os.AsyncTask; import android.os.Bundle; -import com.google.android.material.chip.Chip; -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.appbar.CollapsingToolbarLayout; -import androidx.coordinatorlayout.widget.CoordinatorLayout; -import com.google.android.material.snackbar.Snackbar; -import androidx.fragment.app.Fragment; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -22,17 +13,25 @@ import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestManager; import com.bumptech.glide.request.RequestOptions; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.appbar.CollapsingToolbarLayout; +import com.google.android.material.chip.Chip; +import com.google.android.material.snackbar.Snackbar; import javax.inject.Inject; import javax.inject.Named; import SubscribedUserDatabase.SubscribedUserDao; -import SubscribedUserDatabase.SubscribedUserData; import SubscribedUserDatabase.SubscribedUserRoomDatabase; import User.UserDao; import User.UserData; import User.UserRoomDatabase; import User.UserViewModel; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProviders; import butterknife.BindView; import butterknife.ButterKnife; import jp.wasabeef.glide.transformations.RoundedCornersTransformation; @@ -246,7 +245,7 @@ public class ViewUserDetailActivity extends AppCompatActivity { } @Override - public void onFetchUserDataFail() { + public void onFetchUserDataFailed() { makeSnackbar(R.string.cannot_fetch_user_info); } }); @@ -295,7 +294,7 @@ public class ViewUserDetailActivity extends AppCompatActivity { } } - private static class CheckIsFollowingUserAsyncTask extends AsyncTask { + /*private static class CheckIsFollowingUserAsyncTask extends AsyncTask { private SubscribedUserDao subscribedUserDao; private String userName; @@ -329,5 +328,5 @@ public class ViewUserDetailActivity extends AppCompatActivity { checkIsFollowingUserListener.isNotSubscribed(); } } - } + }*/ } diff --git a/app/src/main/res/layout/fragment_user_listing.xml b/app/src/main/res/layout/fragment_user_listing.xml new file mode 100644 index 00000000..b69ed7ae --- /dev/null +++ b/app/src/main/res/layout/fragment_user_listing.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_user_listing.xml b/app/src/main/res/layout/item_user_listing.xml new file mode 100644 index 00000000..24ba6102 --- /dev/null +++ b/app/src/main/res/layout/item_user_listing.xml @@ -0,0 +1,48 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d286b264..021d5ddf 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,8 +13,9 @@ Error loading image. Tap to retry. Error loading posts.\nTap to retry. - No posts here. + No posts found. No subreddits found. + No users found. Error loading posts Error loading comments