mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-31 06:43:56 +01:00 
			
		
		
		
	Fetch and use thumbnail across the app (#2906)
This commit is contained in:
		
							parent
							
								
									17d69cde02
								
							
						
					
					
						commit
						37e9eae314
					
				
					 18 changed files with 161 additions and 260 deletions
				
			
		|  | @ -42,6 +42,7 @@ public class Media implements Parcelable { | |||
| 
 | ||||
|     // Primary metadata fields | ||||
|     protected Uri localUri; | ||||
|     private String thumbUrl; | ||||
|     protected String imageUrl; | ||||
|     protected String filename; | ||||
|     protected String description; // monolingual description on input... | ||||
|  | @ -93,6 +94,7 @@ public class Media implements Parcelable { | |||
|                  long dataLength, Date dateCreated, Date dateUploaded, String creator) { | ||||
|         this(); | ||||
|         this.localUri = localUri; | ||||
|         this.thumbUrl = imageUrl; | ||||
|         this.imageUrl = imageUrl; | ||||
|         this.filename = filename; | ||||
|         this.description = description; | ||||
|  | @ -107,6 +109,7 @@ public class Media implements Parcelable { | |||
|     @SuppressWarnings("unchecked") | ||||
|     public Media(Parcel in) { | ||||
|         localUri = in.readParcelable(Uri.class.getClassLoader()); | ||||
|         thumbUrl = in.readString(); | ||||
|         imageUrl = in.readString(); | ||||
|         filename = in.readString(); | ||||
|         description = in.readString(); | ||||
|  | @ -124,6 +127,67 @@ public class Media implements Parcelable { | |||
|         descriptions = in.readHashMap(ClassLoader.getSystemClassLoader()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creating Media object from MWQueryPage. | ||||
|      * Earlier only basic details were set for the media object but going forward, | ||||
|      * a full media object(with categories, descriptions, coordinates etc) can be constructed using this method | ||||
|      * | ||||
|      * @param page response from the API | ||||
|      * @return Media object | ||||
|      */ | ||||
|     @Nullable | ||||
|     public static Media from(MwQueryPage page) { | ||||
|         ImageInfo imageInfo = page.imageInfo(); | ||||
|         if (imageInfo == null) { | ||||
|             return null; | ||||
|         } | ||||
|         ExtMetadata metadata = imageInfo.getMetadata(); | ||||
|         if (metadata == null) { | ||||
|             Media media = new Media(null, imageInfo.getOriginalUrl(), | ||||
|                     page.title(), "", 0, null, null, null); | ||||
|             if (!StringUtils.isBlank(imageInfo.getThumbUrl())) { | ||||
|                 media.setThumbUrl(imageInfo.getThumbUrl()); | ||||
|             } | ||||
|             return media; | ||||
|         } | ||||
| 
 | ||||
|         Media media = new Media(null, | ||||
|                 imageInfo.getOriginalUrl(), | ||||
|                 page.title(), | ||||
|                 "", | ||||
|                 0, | ||||
|                 safeParseDate(metadata.dateTimeOriginal().value()), | ||||
|                 safeParseDate(metadata.dateTime().value()), | ||||
|                 StringUtil.fromHtml(metadata.artist().value()).toString() | ||||
|         ); | ||||
| 
 | ||||
|         if (!StringUtils.isBlank(imageInfo.getThumbUrl())) { | ||||
|             media.setThumbUrl(imageInfo.getThumbUrl()); | ||||
|         } | ||||
| 
 | ||||
|         String language = Locale.getDefault().getLanguage(); | ||||
|         if (StringUtils.isBlank(language)) { | ||||
|             language = "default"; | ||||
|         } | ||||
| 
 | ||||
|         media.setDescriptions(Collections.singletonMap(language, metadata.imageDescription().value())); | ||||
|         media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories().value())); | ||||
|         String latitude = metadata.gpsLatitude().value(); | ||||
|         String longitude = metadata.gpsLongitude().value(); | ||||
| 
 | ||||
|         if (!StringUtils.isBlank(latitude) && !StringUtils.isBlank(longitude)) { | ||||
|             LatLng latLng = new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude), 0); | ||||
|             media.setCoordinates(latLng); | ||||
|         } | ||||
| 
 | ||||
|         media.setLicenseInformation(metadata.licenseShortName().value(), metadata.licenseUrl().value()); | ||||
|         return media; | ||||
|     } | ||||
| 
 | ||||
|     public String getThumbUrl() { | ||||
|         return thumbUrl; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets tag of media | ||||
|      * @param key Media key | ||||
|  | @ -322,53 +386,8 @@ public class Media implements Parcelable { | |||
|         return license; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Creating Media object from MWQueryPage. | ||||
|      * Earlier only basic details were set for the media object but going forward, | ||||
|      * a full media object(with categories, descriptions, coordinates etc) can be constructed using this method | ||||
|      * | ||||
|      * @param page response from the API | ||||
|      * @return Media object | ||||
|      */ | ||||
|     @Nullable | ||||
|     public static Media from(MwQueryPage page) { | ||||
|         ImageInfo imageInfo = page.imageInfo(); | ||||
|         if (imageInfo == null) { | ||||
|             return null; | ||||
|         } | ||||
|         ExtMetadata metadata = imageInfo.getMetadata(); | ||||
|         if (metadata == null) { | ||||
|             return new Media(null, imageInfo.getOriginalUrl(), | ||||
|                     page.title(), "", 0, null, null, null); | ||||
|         } | ||||
| 
 | ||||
|         Media media = new Media(null, | ||||
|                 imageInfo.getOriginalUrl(), | ||||
|                 page.title(), | ||||
|                 "", | ||||
|                 0, | ||||
|                 safeParseDate(metadata.dateTimeOriginal().value()), | ||||
|                 safeParseDate(metadata.dateTime().value()), | ||||
|                 StringUtil.fromHtml(metadata.artist().value()).toString() | ||||
|         ); | ||||
| 
 | ||||
|         String language = Locale.getDefault().getLanguage(); | ||||
|         if (StringUtils.isBlank(language)) { | ||||
|             language = "default"; | ||||
|         } | ||||
| 
 | ||||
|         media.setDescriptions(Collections.singletonMap(language, metadata.imageDescription().value())); | ||||
|         media.setCategories(MediaDataExtractorUtil.extractCategoriesFromList(metadata.categories().value())); | ||||
|         String latitude = metadata.gpsLatitude().value(); | ||||
|         String longitude = metadata.gpsLongitude().value(); | ||||
| 
 | ||||
|         if (!StringUtils.isBlank(latitude) && !StringUtils.isBlank(longitude)) { | ||||
|             LatLng latLng = new LatLng(Double.parseDouble(latitude), Double.parseDouble(longitude), 0); | ||||
|             media.setCoordinates(latLng); | ||||
|         } | ||||
| 
 | ||||
|         media.setLicenseInformation(metadata.licenseShortName().value(), metadata.licenseUrl().value()); | ||||
|         return media; | ||||
|     public void setThumbUrl(String thumbUrl) { | ||||
|         this.thumbUrl = thumbUrl; | ||||
|     } | ||||
| 
 | ||||
|     public String getLicenseUrl() { | ||||
|  | @ -482,6 +501,7 @@ public class Media implements Parcelable { | |||
|     @Override | ||||
|     public void writeToParcel(Parcel parcel, int flags) { | ||||
|         parcel.writeParcelable(localUri, flags); | ||||
|         parcel.writeString(thumbUrl); | ||||
|         parcel.writeString(imageUrl); | ||||
|         parcel.writeString(filename); | ||||
|         parcel.writeString(description); | ||||
|  |  | |||
|  | @ -1,7 +1,5 @@ | |||
| package fr.free.nrw.commons; | ||||
| 
 | ||||
| import android.text.Html; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Singleton; | ||||
| 
 | ||||
|  | @ -36,7 +34,7 @@ public class MediaDataExtractor { | |||
|      * @return full Media object with all details including deletion status and talk page | ||||
|      */ | ||||
|     public Single<Media> fetchMediaDetails(String filename) { | ||||
|         Single<Media> mediaSingle = okHttpJsonApiClient.getMedia(filename, false); | ||||
|         Single<Media> mediaSingle = getMediaFromFileName(filename); | ||||
|         Single<Boolean> pageExistsSingle = mediaWikiApi.pageExists("Commons:Deletion_requests/" + filename); | ||||
|         Single<String> discussionSingle = getDiscussion(filename); | ||||
|         return Single.zip(mediaSingle, pageExistsSingle, discussionSingle, (media, deletionStatus, discussion) -> { | ||||
|  | @ -48,6 +46,15 @@ public class MediaDataExtractor { | |||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Method can be used to fetch media for a given filename | ||||
|      * @param filename Eg. File:Test.jpg | ||||
|      * @return return data rich Media object | ||||
|      */ | ||||
|     public Single<Media> getMediaFromFileName(String filename) { | ||||
|         return okHttpJsonApiClient.getMedia(filename, false); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetch talk page from the MediaWiki API | ||||
|      * @param filename | ||||
|  |  | |||
|  | @ -1,123 +0,0 @@ | |||
| package fr.free.nrw.commons; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.text.TextUtils; | ||||
| import android.util.AttributeSet; | ||||
| 
 | ||||
| import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder; | ||||
| import com.facebook.drawee.view.SimpleDraweeView; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.collection.LruCache; | ||||
| import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; | ||||
| import fr.free.nrw.commons.di.ApplicationlessInjection; | ||||
| import fr.free.nrw.commons.mwapi.MediaWikiApi; | ||||
| import io.reactivex.Single; | ||||
| import io.reactivex.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.disposables.CompositeDisposable; | ||||
| import io.reactivex.disposables.Disposable; | ||||
| import io.reactivex.schedulers.Schedulers; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| public class MediaWikiImageView extends SimpleDraweeView { | ||||
|     @Inject MediaWikiApi mwApi; | ||||
|     @Inject LruCache<String, String> thumbnailUrlCache; | ||||
| 
 | ||||
|     protected CompositeDisposable compositeDisposable = new CompositeDisposable(); | ||||
| 
 | ||||
|     public MediaWikiImageView(Context context) { | ||||
|         this(context, null); | ||||
|         init(); | ||||
|     } | ||||
| 
 | ||||
|     public MediaWikiImageView(Context context, AttributeSet attrs) { | ||||
|         this(context, attrs, 0); | ||||
|         init(); | ||||
|     } | ||||
| 
 | ||||
|     public MediaWikiImageView(Context context, AttributeSet attrs, int defStyle) { | ||||
|         super(context, attrs, defStyle); | ||||
|         init(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Sets the media. Fetches its thumbnail if necessary. | ||||
|      * @param media the new media | ||||
|      */ | ||||
|     public void setMedia(Media media) { | ||||
|         if (media == null) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         Disposable disposable = fetchMediaThumbnail(media) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(AndroidSchedulers.mainThread()) | ||||
|                 .subscribe(thumbnail -> { | ||||
|                     if (!StringUtils.isBlank(thumbnail)) { | ||||
|                         setImageUrl(thumbnail); | ||||
|                     } | ||||
|                 }, throwable -> Timber.e(throwable, "Error occurred while fetching thumbnail")); | ||||
| 
 | ||||
|         compositeDisposable.add(disposable); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onDetachedFromWindow() { | ||||
|         compositeDisposable.clear(); | ||||
|         super.onDetachedFromWindow(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Initializes MediaWikiImageView. | ||||
|      */ | ||||
|     private void init() { | ||||
|         ApplicationlessInjection | ||||
|                 .getInstance(getContext() | ||||
|                         .getApplicationContext()) | ||||
|                 .getCommonsApplicationComponent() | ||||
|                 .inject(this); | ||||
|         setHierarchy(GenericDraweeHierarchyBuilder | ||||
|                 .newInstance(getResources()) | ||||
|                 .setPlaceholderImage(VectorDrawableCompat.create(getResources(), | ||||
|                         R.drawable.ic_image_black_24dp, getContext().getTheme())) | ||||
|                 .setFailureImage(VectorDrawableCompat.create(getResources(), | ||||
|                         R.drawable.ic_image_black_24dp, getContext().getTheme())) | ||||
|                 .build()); | ||||
|     } | ||||
| 
 | ||||
|     //TODO: refactor the logic for thumbnails. ImageInfo API can be used to fetch thumbnail upfront | ||||
| 
 | ||||
|     /** | ||||
|      * Fetches media thumbnail from the server | ||||
|      * | ||||
|      * @param media | ||||
|      * @return | ||||
|      */ | ||||
|     public Single<String> fetchMediaThumbnail(Media media) { | ||||
|         if (media.getFilename() != null && thumbnailUrlCache.get(media.getFilename()) != null) { | ||||
|             return Single.just(thumbnailUrlCache.get(media.getFilename())); | ||||
|         } | ||||
|         return mwApi.findThumbnailByFilename(media.getFilename()) | ||||
|                 .map(result -> { | ||||
|                     if (TextUtils.isEmpty(result) && media.getLocalUri() != null) { | ||||
|                         return media.getLocalUri().toString(); | ||||
|                     } else { | ||||
|                         thumbnailUrlCache.put(media.getFilename(), result); | ||||
|                         return result; | ||||
|                     } | ||||
|                 }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Displays the image from the URL. | ||||
|      * @param url the URL of the image | ||||
|      */ | ||||
|     private void setImageUrl(@Nullable String url) { | ||||
|         setImageURI(url); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | @ -8,12 +8,13 @@ import android.view.ViewGroup; | |||
| import android.widget.ArrayAdapter; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import com.facebook.drawee.view.SimpleDraweeView; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| import fr.free.nrw.commons.Media; | ||||
| import fr.free.nrw.commons.MediaWikiImageView; | ||||
| import fr.free.nrw.commons.R; | ||||
| 
 | ||||
| /** | ||||
|  | @ -82,12 +83,12 @@ public class GridViewAdapter extends ArrayAdapter { | |||
|         } | ||||
| 
 | ||||
|         Media item = data.get(position); | ||||
|         MediaWikiImageView imageView = convertView.findViewById(R.id.categoryImageView); | ||||
|         SimpleDraweeView imageView = convertView.findViewById(R.id.categoryImageView); | ||||
|         TextView fileName = convertView.findViewById(R.id.categoryImageTitle); | ||||
|         TextView author = convertView.findViewById(R.id.categoryImageAuthor); | ||||
|         fileName.setText(item.getDisplayTitle()); | ||||
|         setAuthorView(item, author); | ||||
|         imageView.setMedia(item); | ||||
|         imageView.setImageURI(item.getThumbUrl()); | ||||
|         return convertView; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,23 +6,37 @@ import android.widget.LinearLayout; | |||
| import android.widget.ProgressBar; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import com.facebook.drawee.view.SimpleDraweeView; | ||||
| 
 | ||||
| import javax.inject.Inject; | ||||
| 
 | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.OnClick; | ||||
| import fr.free.nrw.commons.MediaWikiImageView; | ||||
| import fr.free.nrw.commons.MediaDataExtractor; | ||||
| import fr.free.nrw.commons.R; | ||||
| import fr.free.nrw.commons.ViewHolder; | ||||
| import fr.free.nrw.commons.contributions.model.DisplayableContribution; | ||||
| import fr.free.nrw.commons.di.ApplicationlessInjection; | ||||
| import io.reactivex.disposables.CompositeDisposable; | ||||
| import io.reactivex.disposables.Disposable; | ||||
| import io.reactivex.schedulers.Schedulers; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| class ContributionViewHolder implements ViewHolder<DisplayableContribution> { | ||||
|     @BindView(R.id.contributionImage) MediaWikiImageView imageView; | ||||
| public class ContributionViewHolder implements ViewHolder<DisplayableContribution> { | ||||
|     @BindView(R.id.contributionImage) | ||||
|     SimpleDraweeView imageView; | ||||
|     @BindView(R.id.contributionTitle) TextView titleView; | ||||
|     @BindView(R.id.contributionState) TextView stateView; | ||||
|     @BindView(R.id.contributionSequenceNumber) TextView seqNumView; | ||||
|     @BindView(R.id.contributionProgress) ProgressBar progressView; | ||||
|     @BindView(R.id.failed_image_options) LinearLayout failedImageOptions; | ||||
| 
 | ||||
|     @Inject | ||||
|     MediaDataExtractor mediaDataExtractor; | ||||
| 
 | ||||
|     private DisplayableContribution contribution; | ||||
|     private CompositeDisposable compositeDisposable = new CompositeDisposable(); | ||||
| 
 | ||||
|     ContributionViewHolder(View parent) { | ||||
|         ButterKnife.bind(this, parent); | ||||
|  | @ -30,8 +44,10 @@ class ContributionViewHolder implements ViewHolder<DisplayableContribution> { | |||
| 
 | ||||
|     @Override | ||||
|     public void bindModel(Context context, DisplayableContribution contribution) { | ||||
|         ApplicationlessInjection.getInstance(context) | ||||
|                 .getCommonsApplicationComponent().inject(this); | ||||
|         this.contribution = contribution; | ||||
|         imageView.setMedia(contribution); | ||||
|         fetchAndDisplayThumbnail(contribution); | ||||
|         titleView.setText(contribution.getDisplayTitle()); | ||||
| 
 | ||||
|         seqNumView.setText(String.valueOf(contribution.getPosition() + 1)); | ||||
|  | @ -71,6 +87,26 @@ class ContributionViewHolder implements ViewHolder<DisplayableContribution> { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * This method fetches the thumbnail url from file name | ||||
|      * This can be removed once #2904 is in place and contribution contains all metadata beforehand | ||||
|      * @param contribution | ||||
|      */ | ||||
|     private void fetchAndDisplayThumbnail(DisplayableContribution contribution) { | ||||
|         Timber.d("Fetching thumbnail for %s", contribution.getFilename()); | ||||
|         Disposable disposable = mediaDataExtractor.getMediaFromFileName(contribution.getFilename()) | ||||
|                 .subscribeOn(Schedulers.io()) | ||||
|                 .observeOn(Schedulers.io()) | ||||
|                 .subscribe(media -> { | ||||
|                     imageView.setImageURI(media.getThumbUrl()); | ||||
|                 }); | ||||
|         compositeDisposable.add(disposable); | ||||
|     } | ||||
| 
 | ||||
|     public void clear() { | ||||
|         compositeDisposable.clear(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Retry upload when it is failed | ||||
|      */ | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ class ContributionsListAdapter extends CursorAdapter { | |||
|         final ContributionViewHolder views = (ContributionViewHolder)view.getTag(); | ||||
|         final Contribution contribution = contributionDao.fromCursor(cursor); | ||||
| 
 | ||||
|         Timber.d("Cursor position is %d", cursor.getPosition()); | ||||
|         DisplayableContribution displayableContribution = new DisplayableContribution(contribution, | ||||
|                 cursor.getPosition(), | ||||
|                 new DisplayableContribution.ContributionActions() { | ||||
|  |  | |||
|  | @ -7,8 +7,8 @@ import dagger.android.AndroidInjectionModule; | |||
| import dagger.android.AndroidInjector; | ||||
| import dagger.android.support.AndroidSupportInjectionModule; | ||||
| import fr.free.nrw.commons.CommonsApplication; | ||||
| import fr.free.nrw.commons.MediaWikiImageView; | ||||
| import fr.free.nrw.commons.auth.LoginActivity; | ||||
| import fr.free.nrw.commons.contributions.ContributionViewHolder; | ||||
| import fr.free.nrw.commons.contributions.ContributionsSyncAdapter; | ||||
| import fr.free.nrw.commons.modifications.ModificationsSyncAdapter; | ||||
| import fr.free.nrw.commons.nearby.PlaceRenderer; | ||||
|  | @ -36,8 +36,6 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application | |||
| 
 | ||||
|     void inject(ModificationsSyncAdapter syncAdapter); | ||||
| 
 | ||||
|     void inject(MediaWikiImageView mediaWikiImageView); | ||||
| 
 | ||||
|     void inject(LoginActivity activity); | ||||
| 
 | ||||
|     void inject(SettingsFragment fragment); | ||||
|  | @ -53,6 +51,8 @@ public interface CommonsApplicationComponent extends AndroidInjector<Application | |||
| 
 | ||||
|     void inject(PicOfDayAppWidget picOfDayAppWidget); | ||||
| 
 | ||||
|     void inject(ContributionViewHolder viewHolder); | ||||
| 
 | ||||
|     @Component.Builder | ||||
|     @SuppressWarnings({"WeakerAccess", "unused"}) | ||||
|     interface Builder { | ||||
|  |  | |||
|  | @ -5,12 +5,12 @@ import android.view.View; | |||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import com.facebook.drawee.view.SimpleDraweeView; | ||||
| import com.pedrogomez.renderers.Renderer; | ||||
| 
 | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import fr.free.nrw.commons.Media; | ||||
| import fr.free.nrw.commons.MediaWikiImageView; | ||||
| import fr.free.nrw.commons.R; | ||||
| 
 | ||||
| /** | ||||
|  | @ -19,8 +19,7 @@ import fr.free.nrw.commons.R; | |||
| class SearchImagesRenderer extends Renderer<Media> { | ||||
|     @BindView(R.id.categoryImageTitle) TextView tvImageName; | ||||
|     @BindView(R.id.categoryImageAuthor) TextView categoryImageAuthor; | ||||
|     @BindView(R.id.categoryImageView) | ||||
|     MediaWikiImageView browseImage; | ||||
|     @BindView(R.id.categoryImageView) SimpleDraweeView browseImage; | ||||
| 
 | ||||
|     private final ImageClickedListener listener; | ||||
| 
 | ||||
|  | @ -52,7 +51,7 @@ class SearchImagesRenderer extends Renderer<Media> { | |||
|     public void render() { | ||||
|         Media item = getContent(); | ||||
|         tvImageName.setText(item.getDisplayTitle()); | ||||
|         browseImage.setMedia(item); | ||||
|         browseImage.setImageURI(item.getThumbUrl()); | ||||
|         setAuthorView(item, categoryImageAuthor); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,11 @@ import android.widget.Spinner; | |||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
| 
 | ||||
| import com.facebook.drawee.backends.pipeline.Fresco; | ||||
| import com.facebook.drawee.interfaces.DraweeController; | ||||
| import com.facebook.drawee.view.SimpleDraweeView; | ||||
| import com.facebook.imagepipeline.request.ImageRequest; | ||||
| 
 | ||||
| import org.apache.commons.lang3.StringUtils; | ||||
| import org.wikipedia.util.DateUtil; | ||||
| import org.wikipedia.util.StringUtil; | ||||
|  | @ -37,7 +42,6 @@ import butterknife.ButterKnife; | |||
| import butterknife.OnClick; | ||||
| import fr.free.nrw.commons.Media; | ||||
| import fr.free.nrw.commons.MediaDataExtractor; | ||||
| import fr.free.nrw.commons.MediaWikiImageView; | ||||
| import fr.free.nrw.commons.R; | ||||
| import fr.free.nrw.commons.Utils; | ||||
| import fr.free.nrw.commons.category.CategoryDetailsActivity; | ||||
|  | @ -52,12 +56,6 @@ import io.reactivex.Single; | |||
| import io.reactivex.android.schedulers.AndroidSchedulers; | ||||
| import io.reactivex.disposables.Disposable; | ||||
| import io.reactivex.schedulers.Schedulers; | ||||
| import java.io.IOException; | ||||
| import java.util.ArrayList; | ||||
| import java.util.Date; | ||||
| import java.util.Locale; | ||||
| import javax.inject.Inject; | ||||
| import javax.inject.Provider; | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| import static android.view.View.GONE; | ||||
|  | @ -100,7 +98,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { | |||
|     private int initialListTop = 0; | ||||
| 
 | ||||
|     @BindView(R.id.mediaDetailImage) | ||||
|     MediaWikiImageView image; | ||||
|     SimpleDraweeView image; | ||||
|     @BindView(R.id.mediaDetailSpacer) | ||||
|     MediaDetailSpacer spacer; | ||||
|     @BindView(R.id.mediaDetailTitle) | ||||
|  | @ -265,7 +263,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { | |||
| 
 | ||||
|     private void displayMediaDetails() { | ||||
|         //Always load image from Internet to allow viewing the desc, license, and cats | ||||
|         image.setMedia(media); | ||||
|         setupImageView(); | ||||
|         title.setText(media.getDisplayTitle()); | ||||
|         desc.setHtmlText(media.getDescription()); | ||||
|         license.setText(media.getLicense()); | ||||
|  | @ -277,6 +275,20 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { | |||
|         compositeDisposable.add(disposable); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Uses two image sources. | ||||
|      * - low resolution thumbnail is shown initially | ||||
|      * - when the high resolution image is available, it replaces the low resolution image | ||||
|      */ | ||||
|     private void setupImageView() { | ||||
|         DraweeController controller = Fresco.newDraweeControllerBuilder() | ||||
|                 .setLowResImageRequest(ImageRequest.fromUri(media.getThumbUrl())) | ||||
|                 .setImageRequest(ImageRequest.fromUri(media.getImageUrl())) | ||||
|                 .setOldController(image.getController()) | ||||
|                 .build(); | ||||
|         image.setController(controller); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         if (layoutListener != null && getView() != null) { | ||||
|  | @ -297,6 +309,7 @@ public class MediaDetailFragment extends CommonsDaggerSupportFragment { | |||
| 
 | ||||
|     private void setTextFields(Media media) { | ||||
|         this.media = media; | ||||
|         setupImageView(); | ||||
|         desc.setHtmlText(prettyDescription(media)); | ||||
|         license.setText(prettyLicense(media)); | ||||
|         coordinates.setText(prettyCoordinates(media)); | ||||
|  |  | |||
|  | @ -52,7 +52,6 @@ import timber.log.Timber; | |||
|  * @author Addshore | ||||
|  */ | ||||
| public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { | ||||
|     private static final String THUMB_SIZE = "640"; | ||||
|     private AbstractHttpClient httpClient; | ||||
|     private CustomMwApi api; | ||||
|     private CustomMwApi wikidataApi; | ||||
|  | @ -293,18 +292,6 @@ public class ApacheHttpClientMediaWikiApi implements MediaWikiApi { | |||
|                 .getString("/api/edit/@result"); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Single<String> findThumbnailByFilename(String filename) { | ||||
|         return Single.fromCallable(() -> api.action("query") | ||||
|                 .param("format", "xml") | ||||
|                 .param("prop", "imageinfo") | ||||
|                 .param("iiprop", "url") | ||||
|                 .param("iiurlwidth", THUMB_SIZE) | ||||
|                 .param("titles", filename) | ||||
|                 .get() | ||||
|                 .getString("/api/query/pages/page/imageinfo/ii/@thumburl")); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Single<String> parseWikicode(String source) { | ||||
|         return Single.fromCallable(() -> api.action("flow-parsoid-utils") | ||||
|  |  | |||
|  | @ -34,8 +34,6 @@ public interface MediaWikiApi { | |||
| 
 | ||||
|     Single<Boolean> pageExists(String pageName); | ||||
| 
 | ||||
|     Single<String> findThumbnailByFilename(String filename); | ||||
| 
 | ||||
|     List<String> getSubCategoryList(String categoryName); | ||||
| 
 | ||||
|     List<String> getParentCategoryList(String categoryName); | ||||
|  |  | |||
|  | @ -47,6 +47,7 @@ import timber.log.Timber; | |||
|  */ | ||||
| @Singleton | ||||
| public class OkHttpJsonApiClient { | ||||
|     private static final String THUMB_SIZE = "640"; | ||||
| 
 | ||||
|     public static final Type mapType = new TypeToken<Map<String, String>>() { | ||||
|     }.getType(); | ||||
|  | @ -274,6 +275,7 @@ public class OkHttpJsonApiClient { | |||
|     private HttpUrl.Builder appendMediaProperties(HttpUrl.Builder builder) { | ||||
|         builder.addQueryParameter("prop", "imageinfo") | ||||
|                 .addQueryParameter("iiprop", "url|extmetadata") | ||||
|                 .addQueryParameter("iiurlwidth", THUMB_SIZE) | ||||
|                 .addQueryParameter("iiextmetadatafilter", "DateTime|Categories|GPSLatitude|GPSLongitude|ImageDescription|DateTimeOriginal|Artist|LicenseShortName|LicenseUrl"); | ||||
| 
 | ||||
|         String language = Locale.getDefault().getLanguage(); | ||||
|  |  | |||
|  | @ -1,38 +0,0 @@ | |||
| package fr.free.nrw.commons.utils; | ||||
| 
 | ||||
| import android.view.View; | ||||
| 
 | ||||
| /** | ||||
|  * This class includes utilities for contribution list fragment indicators, such as number of | ||||
|  * uploads, notification and nearby cards and their progress bar behind them. | ||||
|  */ | ||||
| public class ContributionListViewUtils { | ||||
| 
 | ||||
|     /** | ||||
|      * Sets indicator and progress bar visibility according to 3 states, data is ready to display, | ||||
|      * data still loading, both should be invisible because media details fragment is visible | ||||
|      * @param indicator this can be numOfUploads text view, notification/nearby card views | ||||
|      * @param progressBar this is the progress bar behind indicators, displays they are loading | ||||
|      * @param isIndicatorReady is indicator fetched the information will be displayed | ||||
|      * @param isBothInvisible true if contribution list fragment is not active (ie. Media Details Fragment is active) | ||||
|      */ | ||||
|     public static void setIndicatorVisibility(View indicator, View progressBar, boolean isIndicatorReady, boolean isBothInvisible) { | ||||
|         if (indicator!=null && progressBar!=null) { | ||||
|             if (isIndicatorReady) { | ||||
|                 // Indicator ready, display them | ||||
|                 indicator.setVisibility(View.VISIBLE); | ||||
|                 progressBar.setVisibility(View.GONE); | ||||
|             } else { | ||||
|                 if (isBothInvisible) { | ||||
|                     //  Media Details Fragment is visible, hide both | ||||
|                     indicator.setVisibility(View.GONE); | ||||
|                     progressBar.setVisibility(View.GONE); | ||||
|                 } else { | ||||
|                     //  Indicator is not ready, still loading | ||||
|                     indicator.setVisibility(View.GONE); | ||||
|                     progressBar.setVisibility(View.VISIBLE); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -82,7 +82,7 @@ public class PicOfDayAppWidget extends AppWidgetProvider { | |||
|                                 PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, viewIntent, 0); | ||||
|                                 views.setOnClickPendingIntent(R.id.appwidget_image, pendingIntent); | ||||
| 
 | ||||
|                                 loadImageFromUrl(response.getImageUrl(), context, views, appWidgetManager, appWidgetId); | ||||
|                                 loadImageFromUrl(response.getThumbUrl(), context, views, appWidgetManager, appWidgetId); | ||||
|                             } | ||||
|                         }, | ||||
|                         t -> Timber.e(t, "Fetching picture of the day failed") | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vivek Maskara
						Vivek Maskara