[GSoC] Added option to set a new avatar (#3892)

* Fixes #3861 Use the APIs to fetch leaderboard’s based on uploads via mobile app (all time) and display it in the Leaderboard screen.

* Added option to set a new avatar
This commit is contained in:
Madhur Gupta 2020-08-09 16:06:32 +05:30 committed by GitHub
parent 5f77f610f5
commit bc0b5c05c7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 182 additions and 23 deletions

View file

@ -22,15 +22,19 @@ import butterknife.ButterKnife;
import com.google.android.material.snackbar.Snackbar; import com.google.android.material.snackbar.Snackbar;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.bookmarks.Bookmark; import fr.free.nrw.commons.bookmarks.Bookmark;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesDao;
import fr.free.nrw.commons.contributions.Contribution; import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; import fr.free.nrw.commons.di.CommonsDaggerSupportFragment;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import fr.free.nrw.commons.utils.DownloadUtils; import fr.free.nrw.commons.utils.DownloadUtils;
import fr.free.nrw.commons.utils.ImageUtils; import fr.free.nrw.commons.utils.ImageUtils;
import fr.free.nrw.commons.utils.NetworkUtils; import fr.free.nrw.commons.utils.NetworkUtils;
import fr.free.nrw.commons.utils.ViewUtil; import fr.free.nrw.commons.utils.ViewUtil;
import io.reactivex.disposables.CompositeDisposable;
import java.util.Objects;
import javax.inject.Inject; import javax.inject.Inject;
import timber.log.Timber; import timber.log.Timber;
@ -38,6 +42,14 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
@Inject BookmarkPicturesDao bookmarkDao; @Inject BookmarkPicturesDao bookmarkDao;
@Inject
OkHttpJsonApiClient okHttpJsonApiClient;
@Inject
SessionManager sessionManager;
private static CompositeDisposable compositeDisposable = new CompositeDisposable();
@BindView(R.id.mediaDetailsPager) ViewPager pager; @BindView(R.id.mediaDetailsPager) ViewPager pager;
private Boolean editable; private Boolean editable;
private boolean isFeaturedImage; private boolean isFeaturedImage;
@ -159,6 +171,10 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
// Set wallpaper // Set wallpaper
setWallpaper(m); setWallpaper(m);
return true; return true;
case R.id.menu_set_as_avatar:
// Set avatar
setAvatar(m);
return true;
default: default:
return super.onOptionsItemSelected(item); return super.onOptionsItemSelected(item);
} }
@ -177,6 +193,20 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
ImageUtils.setWallpaperFromImageUrl(getActivity(), Uri.parse(media.getImageUrl())); ImageUtils.setWallpaperFromImageUrl(getActivity(), Uri.parse(media.getImageUrl()));
} }
/**
* Set the media as user's leaderboard avatar
* @param media
*/
private void setAvatar(Media media) {
if (media.getImageUrl() == null || media.getImageUrl().isEmpty()) {
Timber.d("Media URL not present");
return;
}
ImageUtils.setAvatarFromImageUrl(getActivity(), media.getImageUrl(),
Objects.requireNonNull(sessionManager.getCurrentAccount()).name,
okHttpJsonApiClient, compositeDisposable);
}
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (!editable) { // Disable menu options for editable views if (!editable) { // Disable menu options for editable views

View file

@ -12,6 +12,7 @@ import fr.free.nrw.commons.nearby.model.NearbyResultItem;
import fr.free.nrw.commons.profile.achievements.FeaturedImages; import fr.free.nrw.commons.profile.achievements.FeaturedImages;
import fr.free.nrw.commons.profile.achievements.FeedbackResponse; import fr.free.nrw.commons.profile.achievements.FeedbackResponse;
import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse; import fr.free.nrw.commons.profile.leaderboard.LeaderboardResponse;
import fr.free.nrw.commons.profile.leaderboard.UpdateAvatarResponse;
import fr.free.nrw.commons.upload.FileUtils; import fr.free.nrw.commons.upload.FileUtils;
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem; import fr.free.nrw.commons.upload.structure.depictions.DepictedItem;
import fr.free.nrw.commons.utils.ConfigUtils; import fr.free.nrw.commons.utils.ConfigUtils;
@ -103,6 +104,38 @@ public class OkHttpJsonApiClient {
}); });
} }
@NonNull
public Single<UpdateAvatarResponse> setAvatar(String username, String avatar) {
final String urlTemplate = wikiMediaTestToolforgeUrl
+ "/update_avatar.py";
return Single.fromCallable(() -> {
String url = String.format(Locale.ENGLISH,
urlTemplate,
username,
avatar);
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
urlBuilder.addQueryParameter("user", username);
urlBuilder.addQueryParameter("avatar", avatar);
Timber.i("Url %s", urlBuilder.toString());
Request request = new Request.Builder()
.url(urlBuilder.toString())
.build();
Response response = okHttpClient.newCall(request).execute();
if (response != null && response.body() != null && response.isSuccessful()) {
String json = response.body().string();
if (json == null) {
return null;
}
try {
return gson.fromJson(json, UpdateAvatarResponse.class);
} catch (Exception e) {
return new UpdateAvatarResponse();
}
}
return null;
});
}
@NonNull @NonNull
public Single<Integer> getUploadCount(String userName) { public Single<Integer> getUploadCount(String userName) {
HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder(); HttpUrl.Builder urlBuilder = wikiMediaToolforgeUrl.newBuilder();

View file

@ -6,8 +6,6 @@ public class LeaderboardConstants {
public static final int START_OFFSET = 0; public static final int START_OFFSET = 0;
public static final String AVATAR_SOURCE_URL = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0a/%s/1024px-%s.png";
public final static String LOADING = "Loading"; public final static String LOADING = "Loading";
public final static String LOADED = "Loaded"; public final static String LOADED = "Loaded";

View file

@ -1,7 +1,5 @@
package fr.free.nrw.commons.profile.leaderboard; package fr.free.nrw.commons.profile.leaderboard;
import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.AVATAR_SOURCE_URL;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -72,9 +70,7 @@ public class LeaderboardListAdapter extends PagedListAdapter<LeaderboardList, Le
rank.setText(getItem(position).getRank().toString()); rank.setText(getItem(position).getRank().toString());
avatar.setImageURI( avatar.setImageURI(Uri.parse(getItem(position).getAvatar()));
Uri.parse(String.format(AVATAR_SOURCE_URL, getItem(position).getAvatar(),
getItem(position).getAvatar())));
username.setText(getItem(position).getUsername()); username.setText(getItem(position).getUsername());
count.setText(getItem(position).getCategoryCount().toString()); count.setText(getItem(position).getCategoryCount().toString());
} }

View file

@ -0,0 +1,44 @@
package fr.free.nrw.commons.profile.leaderboard;
import com.google.gson.annotations.Expose;
import com.google.gson.annotations.SerializedName;
public class UpdateAvatarResponse {
@SerializedName("status")
@Expose
private String status;
@SerializedName("message")
@Expose
private String message;
@SerializedName("user")
@Expose
private String user;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
}

View file

@ -1,7 +1,5 @@
package fr.free.nrw.commons.profile.leaderboard; package fr.free.nrw.commons.profile.leaderboard;
import static fr.free.nrw.commons.profile.leaderboard.LeaderboardConstants.AVATAR_SOURCE_URL;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.view.LayoutInflater; import android.view.LayoutInflater;
@ -62,8 +60,7 @@ public class UserDetailAdapter extends RecyclerView.Adapter<UserDetailAdapter.Da
leaderboardResponse.getRank())); leaderboardResponse.getRank()));
avatar.setImageURI( avatar.setImageURI(
Uri.parse(String.format(AVATAR_SOURCE_URL, leaderboardResponse.getAvatar(), Uri.parse(leaderboardResponse.getAvatar()));
leaderboardResponse.getAvatar())));
username.setText(leaderboardResponse.getUsername()); username.setText(leaderboardResponse.getUsername());
count.setText(String.format("%s %d", count.setText(String.format("%s %d",
holder.getContext().getResources().getString(R.string.count_prefix), holder.getContext().getResources().getString(R.string.count_prefix),

View file

@ -7,11 +7,9 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.exifinterface.media.ExifInterface; import androidx.exifinterface.media.ExifInterface;
import com.facebook.common.executors.CallerThreadExecutor; import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference; import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource; import com.facebook.datasource.DataSource;
@ -21,13 +19,15 @@ import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage; import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest; import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder; import com.facebook.imagepipeline.request.ImageRequestBuilder;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.io.IOException; import java.io.IOException;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.location.LatLng;
import timber.log.Timber; import timber.log.Timber;
/** /**
@ -69,7 +69,9 @@ public class ImageUtils {
public static final int FILE_NAME_EXISTS = -4; public static final int FILE_NAME_EXISTS = -4;
static final int NO_CATEGORY_SELECTED = -5; static final int NO_CATEGORY_SELECTED = -5;
private static ProgressDialog progressDialog; private static ProgressDialog progressDialogWallpaper;
private static ProgressDialog progressDialogAvatar;
@IntDef( @IntDef(
flag = true, flag = true,
@ -223,28 +225,78 @@ public class ImageUtils {
}, CallerThreadExecutor.getInstance()); }, CallerThreadExecutor.getInstance());
} }
/**
* Calls the set avatar api to set the image url as user's avatar
* @param context
* @param url
* @param username
* @param okHttpJsonApiClient
* @param compositeDisposable
*/
public static void setAvatarFromImageUrl(Context context, String url, String username,
OkHttpJsonApiClient okHttpJsonApiClient, CompositeDisposable compositeDisposable) {
showSettingAvatarProgressBar(context);
try {
compositeDisposable.add(okHttpJsonApiClient
.setAvatar(username, url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {
if (response != null && response.getStatus().equals("200")) {
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_successfully));
if (progressDialogAvatar != null && progressDialogAvatar.isShowing()) {
progressDialogAvatar.dismiss();
}
}
},
t -> {
Timber.e(t, "Setting Avatar Failed");
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully));
if (progressDialogAvatar != null) {
progressDialogAvatar.cancel();
}
}
));
}
catch (Exception e){
Timber.d(e+"success");
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully));
if (progressDialogAvatar != null) {
progressDialogAvatar.cancel();
}
}
}
private static void setWallpaper(Context context, Bitmap bitmap) { private static void setWallpaper(Context context, Bitmap bitmap) {
WallpaperManager wallpaperManager = WallpaperManager.getInstance(context); WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
try { try {
wallpaperManager.setBitmap(bitmap); wallpaperManager.setBitmap(bitmap);
ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_successfully)); ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_successfully));
if (progressDialog != null && progressDialog.isShowing()) { if (progressDialogWallpaper != null && progressDialogWallpaper.isShowing()) {
progressDialog.dismiss(); progressDialogWallpaper.dismiss();
} }
} catch (IOException e) { } catch (IOException e) {
Timber.e(e, "Error setting wallpaper"); Timber.e(e, "Error setting wallpaper");
ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_unsuccessfully)); ViewUtil.showLongToast(context, context.getString(R.string.wallpaper_set_unsuccessfully));
if (progressDialog != null) { if (progressDialogWallpaper != null) {
progressDialog.cancel(); progressDialogWallpaper.cancel();
} }
} }
} }
private static void showSettingWallpaperProgressBar(Context context) { private static void showSettingWallpaperProgressBar(Context context) {
progressDialog = ProgressDialog.show(context, context.getString(R.string.setting_wallpaper_dialog_title), progressDialogWallpaper = ProgressDialog.show(context, context.getString(R.string.setting_wallpaper_dialog_title),
context.getString(R.string.setting_wallpaper_dialog_message), true); context.getString(R.string.setting_wallpaper_dialog_message), true);
} }
private static void showSettingAvatarProgressBar(Context context) {
progressDialogAvatar = ProgressDialog.show(context, context.getString(R.string.setting_avatar_dialog_title),
context.getString(R.string.setting_avatar_dialog_message), true);
}
/** /**
* Result variable is a result of an or operation of all possible problems. Ie. if result * Result variable is a result of an or operation of all possible problems. Ie. if result
* is 0001 means IMAGE_DARK * is 0001 means IMAGE_DARK

View file

@ -25,5 +25,9 @@
android:id="@+id/menu_set_as_wallpaper" android:id="@+id/menu_set_as_wallpaper"
android:title="@string/menu_set_wallpaper" android:title="@string/menu_set_wallpaper"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/menu_set_as_avatar"
android:title="@string/menu_set_avatar"
app:showAsAction="never" />
</menu> </menu>

View file

@ -656,4 +656,9 @@ Upload your first media by tapping on the add button.</string>
<string name="leaderboard_column_rank">Rank</string> <string name="leaderboard_column_rank">Rank</string>
<string name="leaderboard_column_user">User</string> <string name="leaderboard_column_user">User</string>
<string name="leaderboard_column_count">Count</string> <string name="leaderboard_column_count">Count</string>
<string name="setting_avatar_dialog_title">Set as Leaderboard Avatar</string>
<string name="setting_avatar_dialog_message">Setting as Avatar, please wait</string>
<string name="avatar_set_successfully">Avatar Set Successfully</string>
<string name="avatar_set_unsuccessfully">Error setting new avatar, please try again</string>
<string name="menu_set_avatar">Set as avatar</string>
</resources> </resources>