Add a feature: Search for users. Change some names of interfaces and methods.

This commit is contained in:
Alex Ning 2019-02-23 12:04:16 +08:00
parent 3fa6969b75
commit b1db59fda2
22 changed files with 892 additions and 97 deletions

Binary file not shown.

Binary file not shown.

View File

@ -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();
}
}
}

View File

@ -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.onFetchUserDataFail();
fetchUserDataListener.onFetchUserDataFailed();
}
}
@Override
public void onFailure(@NonNull Call<String> 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<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());
fetchUserListingDataListener.onFetchUserListingDataFailed();
}
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
Log.i("call failed", t.getMessage());
fetchUserListingDataListener.onFetchUserListingDataFailed();
}
});
}

View File

@ -49,7 +49,7 @@ public class LoadUserDataAsyncTask extends AsyncTask<Void, Void, Void> {
}
@Override
public void onFetchUserDataFail() {
public void onFetchUserDataFailed() {
loadUserDataAsyncTaskListener.loadUserDataSuccess("");
}
});

View File

@ -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);

View File

@ -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);
}
}

View File

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

View File

@ -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;
}

View File

@ -228,6 +228,4 @@ public class SubredditListingRecyclerViewAdapter extends PagedListAdapter<Subred
((DataViewHolder) holder).subscribeButton.setVisibility(View.GONE);
}
}
}

View File

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

View File

@ -61,7 +61,7 @@ class UserFollowing {
}
@Override
public void onFetchUserDataFail() {
public void onFetchUserDataFailed() {
}
});

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}*/
}

View File

@ -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();
}
}
}
}*/
}

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator_layout_user_listing_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".UserListingFragment">
<com.lsjwzh.widget.materialloadingprogressbar.CircleProgressBar
android:id="@+id/progress_bar_user_listing_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:mlpb_progress_stoke_width="3dp"
app:mlpb_progress_color="@color/colorAccent"
android:layout_gravity="center_horizontal"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view_user_listing_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/fetch_user_listing_info_linear_layout_user_listing_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:id="@+id/fetch_user_listing_info_image_view_user_listing_fragment"
android:layout_width="150dp"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fetch_user_listing_info_text_view_user_listing_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:gravity="center" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/constraint_layout_item_user_listing"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground">
<pl.droidsonroids.gif.GifImageView
android:id="@+id/user_icon_gif_image_view_item_user_listing"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_gravity="center_vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/user_name_text_view_item_user_listing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="32dp"
android:textColor="@android:color/black"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/user_icon_gif_image_view_item_user_listing"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/subscribe_image_view_item_user_listing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@drawable/baseline_add_white_24"
android:tint="@color/colorPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:clickable="true"
android:focusable="true"
android:background="?attr/selectableItemBackground"
android:visibility="gone"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -13,8 +13,9 @@
<string name="tap_to_retry">Error loading image. Tap to retry.</string>
<string name="load_posts_error">Error loading posts.\nTap to retry.</string>
<string name="no_posts">No posts here.</string>
<string name="no_posts">No posts found.</string>
<string name="no_subreddits">No subreddits found.</string>
<string name="no_users">No users found.</string>
<string name="load_posts_failed">Error loading posts</string>
<string name="load_comment_failed">Error loading comments</string>