diff options
Diffstat (limited to 'app/src/main/java/ml')
17 files changed, 795 insertions, 96 deletions
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<Void, Void, Void> { + 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> 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.onFetchUserDataFailed(); + } + } + + @Override + public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { + Log.i("call failed", t.getMessage()); + fetchUserDataListener.onFetchUserDataFailed(); + } + }); + } + + public static void fetchUserListingData(Retrofit retrofit, String query, String after, + FetchUserListingDataListener fetchUserListingDataListener) { + RedditAPI api = retrofit.create(RedditAPI.class); + + Call<String> userInfo = api.searchUsers(query, after); + userInfo.enqueue(new Callback<String>() { + @Override + public void onResponse(@NonNull Call<String> call, @NonNull retrofit2.Response<String> response) { + if(response.isSuccessful()) { + ParseUserData.parseUserListingData(response.body(), new ParseUserData.ParseUserListingDataListener() { + @Override + public void onParseUserListingDataSuccess(ArrayList<UserData> userData, String after) { + fetchUserListingDataListener.onFetchUserListingDataSuccess(userData, after); + } + + @Override + public void onParseUserListingDataFailed() { + fetchUserListingDataListener.onFetchUserListingDataFailed(); } }); } else { Log.i("call failed", response.message()); - fetchUserDataListener.onFetchUserDataFail(); + fetchUserListingDataListener.onFetchUserListingDataFailed(); } } @Override public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { Log.i("call failed", t.getMessage()); - fetchUserDataListener.onFetchUserDataFail(); + 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<Void, Void, Void> { } @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> 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<Void, Void, Void> { 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<Void, Void, Void> { + private JSONObject jsonResponse; + private ParseUserListingDataListener parseUserListingDataListener; + private String after; + private boolean parseFailed; + + private ArrayList<UserData> 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<String> searchSubreddits(@Query("q") String subredditName, @Query("after") String after); - @GET("profiles/search.json?raw_json=1") - Call<String> searchProfiles(@Query("q") String profileName, @Query("after") String after, - @HeaderMap Map<String, String> headers); + @GET("search.json?raw_json=1&type=user") + Call<String> searchUsers(@Query("q") String profileName, @Query("after") String after); @GET("search.json?raw_json=1&type=link") Call<String> 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<Subred ((DataViewHolder) holder).subscribeButton.setVisibility(View.GONE); } } - - } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/SubredditSubscription.java b/app/src/main/java/ml/docilealligator/infinityforreddit/SubredditSubscription.java index 9ede0db5..ea72db55 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/SubredditSubscription.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/SubredditSubscription.java @@ -58,18 +58,6 @@ class SubredditSubscription { public void onFetchSubredditDataSuccess(SubredditData subredditData, int nCurrentOnlineSubscribers) { new UpdateSubscriptionAsyncTask(subscribedSubredditDao, subredditData, true).execute(); - /*ParseSubredditData.parseSubredditData(response, new ParseSubredditData.ParseSubredditDataListener() { - @Override - public void onParseSubredditDataSuccess(SubredditData subredditData, int nCurrentOnlineSubscribers) { - new UpdateSubscriptionAsyncTask(subscribedSubredditDao, - subredditData, true).execute(); - } - - @Override - public void onParseSubredditDataFail() { - - } - });*/ } @Override diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/UserFollowing.java b/app/src/main/java/ml/docilealligator/infinityforreddit/UserFollowing.java index ec069664..4003c2d4 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/UserFollowing.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/UserFollowing.java @@ -61,7 +61,7 @@ class UserFollowing { } @Override - public void onFetchUserDataFail() { + public void onFetchUserDataFailed() { } }); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingDataSource.java b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingDataSource.java new file mode 100644 index 00000000..9273c32b --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/UserListingDataSource.java @@ -0,0 +1,107 @@ +package ml.docilealligator.infinityforreddit; + +import java.util.ArrayList; + +import User.UserData; +import androidx.annotation.NonNull; +import androidx.lifecycle.MutableLiveData; +import androidx.paging.PageKeyedDataSource; +import retrofit2.Retrofit; + +public class UserListingDataSource extends PageKeyedDataSource<String, UserData> { + interface OnUserListingDataFetchedCallback { + void hasUser(); + void noUser(); + } + private Retrofit retrofit; + private String query; + private UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback; + + private MutableLiveData<NetworkState> paginationNetworkStateLiveData; + private MutableLiveData<NetworkState> initialLoadStateLiveData; + + private PageKeyedDataSource.LoadInitialParams<String> initialParams; + private PageKeyedDataSource.LoadInitialCallback<String, UserData> initialCallback; + private PageKeyedDataSource.LoadParams<String> params; + private PageKeyedDataSource.LoadCallback<String, UserData> 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<String> params, @NonNull PageKeyedDataSource.LoadInitialCallback<String, UserData> callback) { + initialParams = params; + initialCallback = callback; + + initialLoadStateLiveData.postValue(NetworkState.LOADING); + + FetchUserData.fetchUserListingData(retrofit, query, null, new FetchUserData.FetchUserListingDataListener() { + @Override + public void onFetchUserListingDataSuccess(ArrayList<UserData> 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<String> params, @NonNull PageKeyedDataSource.LoadCallback<String, UserData> callback) { + + } + + @Override + public void loadAfter(@NonNull PageKeyedDataSource.LoadParams<String> params, @NonNull PageKeyedDataSource.LoadCallback<String, UserData> 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> 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<UserListingDataSource> 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<UserListingDataSource> 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<UserData, RecyclerView.ViewHolder> { + 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<UserData> DIFF_CALLBACK = new DiffUtil.ItemCallback<UserData>() { + @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<NetworkState> paginationNetworkState; + private LiveData<NetworkState> initialLoadingState; + private LiveData<PagedList<UserData>> Users; + + UserListingViewModel(Retrofit retrofit, String query, + UserListingDataSource.OnUserListingDataFetchedCallback onUserListingDataFetchedCallback) { + UserListingDataSourceFactory = new UserListingDataSourceFactory(retrofit, query, onUserListingDataFetchedCallback); + + initialLoadingState = Transformations.switchMap(UserListingDataSourceFactory.getUserListingDataSourceMutableLiveData(), + (Function<UserListingDataSource, LiveData<NetworkState>>) UserListingDataSource::getInitialLoadStateLiveData); + paginationNetworkState = Transformations.switchMap(UserListingDataSourceFactory.getUserListingDataSourceMutableLiveData(), + (Function<UserListingDataSource, LiveData<NetworkState>>) UserListingDataSource::getPaginationNetworkStateLiveData); + PagedList.Config pagedListConfig = + (new PagedList.Config.Builder()) + .setEnablePlaceholders(false) + .setPageSize(25) + .build(); + + Users = (new LivePagedListBuilder(UserListingDataSourceFactory, pagedListConfig)).build(); + } + + LiveData<PagedList<UserData>> getUsers() { + return Users; + } + + LiveData<NetworkState> getPaginationNetworkState() { + return paginationNetworkState; + } + + LiveData<NetworkState> 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 extends ViewModel> T create(@NonNull Class<T> 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<Void, Void, Void> { - - 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<Void, Void, Void> { + /*private static class CheckIsFollowingUserAsyncTask extends AsyncTask<Void, Void, Void> { private SubscribedUserDao subscribedUserDao; private String userName; @@ -329,5 +328,5 @@ public class ViewUserDetailActivity extends AppCompatActivity { checkIsFollowingUserListener.isNotSubscribed(); } } - } + }*/ } |